內容參考:
阮一峰 - JS運行機制
MDN - Promise構造函數
MDN - 使用Promise
美團Promise解析
偶然回憶起js異步操作的點點滴滴,又想起用了一年的Promise,哎?兄弟,你到底是個什么東西???
先擺個你一定看不懂的圖然后我們慢慢來。(看不懂千萬不要假裝騙自己哦~)

單線程
JavaScript是單線程機制,為什么是單線程因為如果存在兩個線程同時修改dom,dom會不知所措(Android系統也是只允許在主線程中更新UI)
所以既然沒有多線程做并發(fā),只能設計出異步任務。
JavaScript的所有網絡操作,瀏覽器事件,都必須是異步執(zhí)行,那么我們怎么做異步任務呢?
1 ES5回調函數
2 ES6 Promise
3 ES7 async/await
回調函數我們很熟,也知道它的著名缺陷 - 回調煉獄
async1(function(){
async2(function(){
async3(function(
async4(funciton(){
async5(function(){
//(╯°°)╯︵┻━┻
//...
});
});
));
});});
Promise 用鏈式調用解決了這一煩惱:
function doingSome(a){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve(a+1)
},1500)
})
}
doingSome(1).then(function(newResult){
console.log('第一個promise :' + newResult)
return doingSome(newResult)
}).then(function(result){
console.log('第二個promise:'+result)
})
But
知道這些可以滿足我們的好奇心么 ???
它的設計模式,代碼結構才是我鐘意的地方。
去MDN翻翻Promise構造方法的參數,方法,執(zhí)行描述,額,疑問一大堆啊,其中有一句:
Promise 對象是一個代理對象(代理一個值),被代理的值在Promise對象創(chuàng)建時可能是未知的。它允許你為異步操作的成功和失敗分別綁定相應的處理方法(handlers)。 這讓異步方法可以像同步方法那樣返回值,但并不是立即返回最終執(zhí)行結果,而是一個能代表未來出現的結果的promise對象
啥,我們創(chuàng)建一個Promise后,它返回一個新的Promise,為什么不返回自己然后重用?
Promise構造函數執(zhí)行時立即調用executor 函數,executor 函數在Promise構造函數返回新建對象前被調用, resolve 和 reject 兩個函數作為參數傳遞給executor。resolve 和 reject 函數被調用時,分別將promise的狀態(tài)改為fulfilled(完成)或rejected(失敗)
所以說executor到底是什么時候被調用的,resolve和reject做了什么騷操作?
then和catch在executor執(zhí)行后被執(zhí)行,那么failureCallback和successCallback在executor執(zhí)行時還沒傳入,那如果我們的executor中直接同步調用resolve或者reject呢?
new Promise(function(resolve, reject){
resolve()
}).then(function(){
// here is successCallback
})
試驗結果還是可以調用到successCallback,執(zhí)行executor時還在構造函數方法中,都還沒調用then方法,你怎么得到successCallback的???
先做一些小實驗測試一下

首先Promise構造函數并沒有返回值
executor調用了resolve后,將-當前-Promise對象狀態(tài)改為了:resolved
接著實驗:

這里可以確定then方法返回了一個新的Promise
我們去除延遲,再試一遍:

在then方法內部就把successCallback給執(zhí)行了,最后返回新的Promise對象。
這樣看來resolve感覺像是做了setTimeout 0處理,使它處于任務隊列底部,最后被執(zhí)行。
我們怎么知道它的真正實現呢?源碼找不到,我們看看別人博客吧,我看到很多什么自己實現一個Promise這種文章,但是呢,他們的resolve實現都是用setTimeout 0,我就很疑惑,難道瀏覽器也是這么實現的?
其實不準確,我們看規(guī)范好吧,Promise A+規(guī)范
這里有一段話很重要(google翻譯后的)

瀏覽器就是用setTimeout,Nodejs就是process.nextTick(所提及的任務機制暫時不懂,不過可以參考:JS并發(fā)模型)
那resolve的實現過程就應該是這樣的:
function(val) {
var _this= this;
if (this.status == 'pending') {
this.status = 'resolved';
this.value=val;
setTimeout(function() {
_this.resolveFunc(_this.value);
}, 0);
}
}
MDN有一段話也可以幫助我們理解:

為什么then要返回一個新的Promise?
MDN 對于then方法的解釋:

如果successCallback或者failureCallback返回的是一個Promise呢 ?
還有一段解釋:

我想說如果你在回調函數中還返回了一個Promise,就像我們上面用的鏈式調用的例子:
doingSome(1).then(function(newResult){
console.log('第一個promise :' + newResult)
return doingSome(newResult)
}).then(function(result){
console.log('第二個promise:'+result)
})
那么恭喜你,我們這里出現了三個Promise,我們要分析三個Promise在一次鏈式調用中的作用。。。
我們解釋下:“任何被添加給 promise2 的回調函數都會被排在 successCallback 或 failureCallback 返回的 Promise 后面”
上面第二個then添加了一個successCallback,它說會被我們第一個then的successCallback返回的doingSome中的Promise調用,非常亂,不錯有人已經幫我理清楚了,沒錯,我們借鑒一下:
徹底理解Promise對象——用es5語法實現一個自己的Promise(上篇)
我們看下他的then方法怎么寫的:
MyPromise.prototype.then = function(resolveFunc, rejectFunc) {
var self = this;
return new MyPromise(function(resolve_next, reject_next) {
function resolveFuncWrap() {
var result = resolveFunc(self.value);
if (result && typeof result.then === 'function') {
//如果result是MyPromise對象,則通過then將resolve_next和reject_next傳給它
result.then(resolve_next, reject_next);
} else {
//如果result是其他對象,則作為參數傳給resolve_next
resolve_next(result);
}
}
function rejectFuncWrap() {
var result = rejectFunc(self.value);
if (result && typeof result.then === 'function') {
//如果result是MyPromise對象,則通過then將resolve_next和reject_next傳給它
result.then(resolve_next, reject_next);
} else {
//如果result是其他對象,則作為參數傳給resolve_next
resolve_next(result);
}
}
self.resolveFunc = resolveFuncWrap;
self.rejectFunc = rejectFuncWrap;
})
}
最開始調用的Promise我們這里稱為Promise_1,then中直接return的Promise我們稱為 Promise_2,回調函數中返回的Promise我們稱為Promise_3
Promise_2的目的就是鏈式then的書寫,因為then返回的是一個Promise自然可以執(zhí)行then方法。
內部的邏輯就是封裝callback,讓callback執(zhí)行的時候可以執(zhí)行Promise_2的then的successCallback
這個“讓callback執(zhí)行的時候可以執(zhí)行Promise_2的then的successCallback”就對應了兩種情況:
-
Promise_1的callback返回了Promise
我們關鍵代碼:
result.then(resolve_next, reject_next);
將Promise_2和Promise_3神奇的串在了一起,
如果Promise_3調用了resolve,那么就執(zhí)行Promise_2的resolve,Promise_2的resolve不就接著執(zhí)行Promise_2的successCallback么,最后等同于:Promise_2 的successCallback變成了Promise_3的
-
返回了非Promise
直接執(zhí)行Promise_2的successCallback或者failureCallback
錯誤傳播的特性
基本上,一個 Promise 鏈式遇到異常就會停止,查看鏈式的底端,尋找catch處理程序來代替當前執(zhí)行。在同步的代碼執(zhí)行之后,這是非常模型化的。
這化作了兩個特性:
- 遇到異?;蛘哒{用reject會去順著鏈式向下找一個catch執(zhí)行
-
如果遇到異?;蛘哒{用reject,會直接執(zhí)行catch然后繼續(xù)catch下面的代碼,catch之前都不會執(zhí)行
舉例:
function doingSome(a){
return new Promise(function(resolve,reject){
setTimeout(function(){
reject(a+1)
},1500)
})
}
doingSome(1).then(function(result){
console.log(result)
return result
}).then(function(result){
console.log(result)
return result
}).then(function(result1){
console.log(result)
return result
}).catch(function(result){
console.log('failed: ' + result)
return result
}).then(function(result){
console.log(result)
return result
})
輸出:
failed: 2
2
(別的實現細節(jié)暫且不提,錯誤請指正?。?/p>