摘要:如果改變已經(jīng)發(fā)生了,你再對(duì)對(duì)象添加回調(diào)函數(shù),也會(huì)立即得到這個(gè)結(jié)果。其次,如果不設(shè)置回調(diào)函數(shù),內(nèi)部拋出的錯(cuò)誤,不會(huì)反應(yīng)到外部。
作為一個(gè)入門(mén)級(jí)前端,今天是一個(gè)非常值得紀(jì)念的日子,因?yàn)檫@是我第一次在論壇上發(fā)表帖子,作為起步。雖然我覺(jué)得自己水平還是十分的有限,對(duì)一些細(xì)節(jié)的理解還不是很透徹,但是還是要邁出這一步,不管是給別的新手作為學(xué)習(xí)參考,還是自己以后回顧,總覺(jué)得需要把自己的成長(zhǎng)記錄下來(lái),希望自己以后還是要多堅(jiān)持,如果有不對(duì)的地方還是希望大家及時(shí)提出來(lái),共同進(jìn)步
今天有時(shí)間翻到了es6的promise,可能大家都對(duì)此熟悉不過(guò),我之前一直覺(jué)得promise也很簡(jiǎn)單,但是今天確實(shí)讓我對(duì)promise有了一個(gè)新的了解,以前的理解可能是錯(cuò)誤的。。。先來(lái)看看官方的promise的定義是:
所謂Promise,簡(jiǎn)單說(shuō)就是一個(gè)容器,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果。從語(yǔ)法上說(shuō),Promise 是一個(gè)對(duì)象,從它可以獲取異步操作的消息。Promise 提供統(tǒng)一的 API,各種異步操作都可以用同樣的方法進(jìn)行處理。
特點(diǎn):
(1)對(duì)象的狀態(tài)不受外界影響。Promise對(duì)象代表一個(gè)異步操作,有三種狀態(tài):pending(進(jìn)行中)、fulfilled(已成功)和rejected(已失敗)。只有異步操作的結(jié)果,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無(wú)法改變這個(gè)狀態(tài)。這也是Promise這個(gè)名字的由來(lái),它的英語(yǔ)意思就是“承諾”,表示其他手段無(wú)法改變
(2)一旦狀態(tài)改變,就不會(huì)再變,任何時(shí)候都可以得到這個(gè)結(jié)果。Promise對(duì)象的狀態(tài)改變,只有兩種可能:從pending變?yōu)閒ulfilled和從pending變?yōu)閞ejected。只要這兩種情況發(fā)生,狀態(tài)就凝固了,不會(huì)再變了,會(huì)一直保持這個(gè)結(jié)果,這時(shí)就稱(chēng)為 resolved(已定型)。如果改變已經(jīng)發(fā)生了,你再對(duì)Promise對(duì)象添加回調(diào)函數(shù),也會(huì)立即得到這個(gè)結(jié)果。這與事件(Event)完全不同,事件的特點(diǎn)是,如果你錯(cuò)過(guò)了它,再去監(jiān)聽(tīng),是得不到結(jié)果的。
初讀上面這段話我讀了不下5次,但是我還是沒(méi)能真正了解其真正表達(dá)的意思,于是我查閱了部分資料,終于找到了一個(gè)比較容易理解的說(shuō)法,這個(gè)估計(jì)小白應(yīng)該是可以看得懂得。
就拿做飯吃飯洗碗來(lái)舉例子吧,這是三個(gè)步驟,第一步做飯,再第二步吃飯的時(shí)候我們需要拿到第一步做的飯,在第三步洗碗的時(shí)候我們需要拿到第二步的碗筷,而且這三個(gè)步驟必須是按照順序執(zhí)行,有嚴(yán)格的先后順序。
//做飯
function cook(){
console.log("開(kāi)始做飯。");
var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){
console.log("做飯完畢!");
resolve("雞蛋炒飯");
}, 1000);
});
return p;
}
//吃飯
function eat(data){
console.log("開(kāi)始吃飯:" + data);
var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){
console.log("吃飯完畢!");
resolve("一個(gè)碗和一雙筷子");
}, 2000);
});
return p;
}
//洗碗
function wash(data){
console.log("開(kāi)始洗碗:" + data);
var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){
console.log("洗碗完畢!");
resolve("干凈的碗筷");
}, 2000);
});
return p;
}
//函數(shù)調(diào)用
cook().then(res1 => {
console.log(res1,"這是第一步傳遞給第二步的參數(shù)")
return eat(res1)
}).then(res2 => {
console.log(res2,"這是第二步傳給第三步的參數(shù)")
return wash(res2)
}).then(res3 => {
console.log(res3,"飯吃完了還你干凈的碗筷")
})
結(jié)果如下:
看完上面的代碼大家可能會(huì)有好多疑問(wèn),我在這里把我當(dāng)時(shí)初學(xué)promise的疑問(wèn)和大家分享一下:
答:Promise也有一些缺點(diǎn)。首先,無(wú)法取消Promise,一旦新建它就會(huì)立即執(zhí)行,無(wú)法中途取消。其次,如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯(cuò)誤,不會(huì)反應(yīng)到外部。第三,當(dāng)處于pending狀態(tài)時(shí),無(wú)法得知目前進(jìn)展到哪一個(gè)階段(剛剛開(kāi)始還是即將完成)。
這是promise的一些缺點(diǎn),一旦新建它就會(huì)立即執(zhí)行 所以為了控制這個(gè)promise對(duì)象什么時(shí)候執(zhí)行,在開(kāi)發(fā)過(guò)程中我們需要在外面包裹一個(gè)函數(shù),通過(guò)調(diào)用函數(shù)的形式來(lái)控制promise的執(zhí)行。大家可以在自己的編輯器中試試。
new Promise(function(resolve, reject) {
setTimeout(()=> {
console.log("開(kāi)車(chē)?。?!")
},2000)
});
答:因?yàn)?strong>有了Promise對(duì)象,就可以將異步操作以同步操作的流程表達(dá)出來(lái),避免了層層嵌套的回調(diào)函數(shù)。此外,Promise對(duì)象提供統(tǒng)一的接口,使得控制異步操作更加容易。
//函數(shù)調(diào)用
cook().then(res1 => {
console.log(res1,"這是第一步傳遞給第二步的參數(shù)")
return eat(res1)
}).then(res2 => {
console.log(res2,"這是第二步傳給第三步的參數(shù)")
return wash(res2)
}).then(res3 => {
console.log(res3,"飯吃完了還你干凈的碗筷")
})
大家看我的這段代碼 在執(zhí)行了cook()函數(shù)的時(shí)候cook函數(shù)return出來(lái)一個(gè)promise對(duì)象,promise對(duì)象上有.then()或.catch()方法,那么直接cook().then就可以在.then()方法中我們可以拿到promise對(duì)象中向外傳遞的參數(shù),這個(gè)參數(shù)我們將傳遞給下一個(gè)eat()函數(shù),作為eat()函數(shù)的參數(shù)繼續(xù)執(zhí)行。 而eat()函數(shù)也return了一個(gè)promise對(duì)象。那么我們將我們的這段代碼拆解一下:
第一步: cook() //拿到的是cook return出來(lái)的promise對(duì)象
第二步: cook().then(res => {
console.log(res) //拿到內(nèi)部向外部傳遞的參數(shù)
})
第三步: cook().then(res => {
console.log(res)
return eat(res) //eat執(zhí)行以后return的是eat的promise對(duì)象,然后再把這個(gè)對(duì)象繼續(xù)向外return
}) //那么第三步的最終結(jié)果就是eat()的promise對(duì)象
第四步: cook().then(res => {
console.log(res)
return eat(res) //此時(shí)的結(jié)果是eat()的promise對(duì)象
}).then(res2 => {
// 此時(shí)eat的promise又有.then方法 .....以此類(lèi)推
})
我們就這樣一步一步的完成了整個(gè)做飯、吃飯、洗碗的整個(gè)流程。 縱觀以上代碼你會(huì)發(fā)現(xiàn)雖然每一個(gè)操作流程中我都是以異步函數(shù)setTimeout來(lái)進(jìn)行的,但是在調(diào)用過(guò)程中確是按照 做飯-吃飯-洗碗的正常流程表達(dá)的。這不是promise的有了Promise對(duì)象,就可以將異步操作以同步操作的流程表達(dá)出來(lái),避免了層層嵌套的回調(diào)函數(shù)
//做飯
function cook(){
console.log("開(kāi)始做飯。");
var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){
console.log("飯糊了....沒(méi)法吃");
reject("糊了的飯");
}, 1000);
});
return p;
}
//吃飯
function eat(data){
console.log("開(kāi)始吃飯:" + data);
var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){
console.log("吃飯完畢!");
resolve("一個(gè)碗和一雙筷子");
}, 2000);
});
return p;
}
cook().then(res1 => {
console.log(res1,"這是第一步傳遞給第二步的參數(shù)")
return eat(res1)
}).catch(err => {
console.log(err,"返回錯(cuò)誤")
})
catch()方法用來(lái)指定 reject 的回調(diào)。
function tackBus1(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log("甲正在上車(chē)");
resolve("甲坐好了");
}, 1000);
});
return p;
}
function tackBus2(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log("乙正在上車(chē)");
resolve("乙坐好了");
}, 2000);
});
return p;
}
function tackBus3(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log("丙正在上車(chē)");
resolve("丙坐好了");
}, 3000);
});
return p;
}
Promise.all([tackBus1(),tackBus2(),tackBus3()]).then(res => {
console.log(res)
})
司機(jī)在等人上車(chē),在乘客都坐穩(wěn)了以后開(kāi)車(chē)發(fā)車(chē),這就用Promise.all來(lái)執(zhí)行,all接收一個(gè)數(shù)組參數(shù),里面的值最終都算返回Promise對(duì)象。這樣,三個(gè)異步操作的并行執(zhí)行的,等到它們都執(zhí)行完后才會(huì)進(jìn)到then里面。那么,三個(gè)異步操作返回的數(shù)據(jù)哪里去了呢?都在then里面呢,all會(huì)把所有異步操作的結(jié)果放進(jìn)一個(gè)數(shù)組中傳給then,就是上面的results。所以上面代碼的輸出結(jié)果就是:
這是.all的方法,promise還有一種.race的方法,它和all方法的區(qū)別就是: all方法的效果實(shí)際上是誰(shuí)跑的慢,以誰(shuí)為準(zhǔn)執(zhí)行回調(diào),那么相對(duì)的就有另一個(gè)方法誰(shuí)跑的快,以誰(shuí)為準(zhǔn)執(zhí)行回調(diào),剛剛的執(zhí)行結(jié)果我設(shè)置了他們的時(shí)間間隔分別是1s,2s,3s,在等最慢的執(zhí)行完以后才執(zhí)行了all這個(gè)回調(diào)方法,現(xiàn)在咱們來(lái)試試promise.race方法
function tackBus1(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log("甲正在上車(chē)");
resolve("甲坐好了");
}, 1000);
});
return p;
}
function tackBus2(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log("乙正在上車(chē)");
resolve("乙坐好了");
}, 2000);
});
return p;
}
function tackBus3(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log("丙正在上車(chē)");
resolve("丙坐好了");
}, 3000);
});
return p;
}
Promise.race([tackBus1(),tackBus2(),tackBus3()]).then(res => {
console.log(res)
})
結(jié)果如下:
可以看出來(lái)在甲執(zhí)行完畢后立即就執(zhí)行了.race()方法,但是不耽誤其他兩個(gè)異步操作的進(jìn)行,.race()中拿到的參數(shù)也只是當(dāng)前最先執(zhí)行完的異步操作中傳遞出來(lái)的參數(shù)。
在看到promise的時(shí)候有一個(gè)地方還是令我有困惑,現(xiàn)在先留一個(gè)懸念,大家可以先看看下面這段代碼,你覺(jué)得輸出結(jié)果是什么呢? 我們下回見(jiàn)!
setTimeout(function(){
console.log("1")
});
new Promise(function(resolve){
console.log("2");
resolve();
}).then(function(){
console.log("3")
});
console.log("4");
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/6835.html
摘要:事件循環(huán)背景是一門(mén)單線程非阻塞的腳本語(yǔ)言,單線程意味著,代碼在執(zhí)行的任何時(shí)候,都只有一個(gè)主線程來(lái)處理所有的任務(wù)。在意識(shí)到該問(wèn)題之際,新特性中的可以讓成為一門(mén)多線程語(yǔ)言,但實(shí)際開(kāi)發(fā)中使用存在著諸多限制。這個(gè)地方被稱(chēng)為執(zhí)行棧。 事件循環(huán)(Event Loop) 背景 JavaScript是一門(mén)單線程非阻塞的腳本語(yǔ)言,單線程意味著,JavaScript代碼在執(zhí)行的任何時(shí)候,都只有一個(gè)主線程來(lái)...
摘要:這種問(wèn)題在設(shè)置倒計(jì)時(shí)的經(jīng)常遇到,倒計(jì)時(shí)開(kāi)始的時(shí)候設(shè)置的時(shí)間是從服務(wù)器拿到的系統(tǒng)時(shí)間很準(zhǔn)確,但是如果后面不定期像服務(wù)期請(qǐng)求系統(tǒng)時(shí)間進(jìn)行校準(zhǔn)的話,你可能會(huì)發(fā)現(xiàn)倒計(jì)時(shí)的偏差越來(lái)越來(lái)大,這就是主線程執(zhí)行的時(shí)間比設(shè)定的延遲時(shí)間長(zhǎng)導(dǎo)致的。 關(guān)于js執(zhí)行機(jī)制,老早之前就一直想寫(xiě)篇文章做個(gè)總結(jié),因?yàn)楹蚸s執(zhí)行順序的面試題碰到的特別多,每次碰到總是會(huì)去網(wǎng)上查,沒(méi)有系統(tǒng)地總結(jié),搞得每次碰到都是似懂非懂的感...
摘要:如果沒(méi)有其他異步任務(wù)要處理比如到期的定時(shí)器,會(huì)一直停留在這個(gè)階段,等待請(qǐng)求返回結(jié)果。執(zhí)行的執(zhí)行事件關(guān)閉請(qǐng)求的,例如事件循環(huán)的每一次循環(huán)都需要依次經(jīng)過(guò)上述的階段。因此,才會(huì)早于執(zhí)行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任務(wù)(Synchronous) 在主線程上排隊(duì)執(zhí)行的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù) ...
摘要:聲明的變量不得改變值,這意味著,一旦聲明變量,就必須立即初始化,不能留到以后賦值。 雖然今年沒(méi)有換工作的打算 但為了跟上時(shí)代的腳步 還是忍不住整理了一份最新前端知識(shí)點(diǎn) 知識(shí)點(diǎn)匯總 1.HTML HTML5新特性,語(yǔ)義化瀏覽器的標(biāo)準(zhǔn)模式和怪異模式xhtml和html的區(qū)別使用data-的好處meta標(biāo)簽canvasHTML廢棄的標(biāo)簽IE6 bug,和一些定位寫(xiě)法css js放置位置和原因...
摘要:聲明的變量不得改變值,這意味著,一旦聲明變量,就必須立即初始化,不能留到以后賦值。 雖然今年沒(méi)有換工作的打算 但為了跟上時(shí)代的腳步 還是忍不住整理了一份最新前端知識(shí)點(diǎn) 知識(shí)點(diǎn)匯總 1.HTML HTML5新特性,語(yǔ)義化瀏覽器的標(biāo)準(zhǔn)模式和怪異模式xhtml和html的區(qū)別使用data-的好處meta標(biāo)簽canvasHTML廢棄的標(biāo)簽IE6 bug,和一些定位寫(xiě)法css js放置位置和原因...
閱讀 1326·2019-08-30 15:44
閱讀 2033·2019-08-30 13:49
閱讀 1669·2019-08-26 13:54
閱讀 3500·2019-08-26 10:20
閱讀 3292·2019-08-23 17:18
閱讀 3308·2019-08-23 17:05
閱讀 2144·2019-08-23 15:38
閱讀 1027·2019-08-23 14:35