Promise/Bluebird源碼

本做品採用知識共享署名 4.0 國際許可協議進行許可。轉載聯繫做者並保留聲明頭部與原文連接https://luzeshu.com/blog/bluebirdsource
本博客同步在http://www.cnblogs.com/papertree/p/7163870.htmljavascript


時隔一年,把以前結尾還有一部分未完成的博客完成。版本2.9。具體忘了哪一個revision number。不過原理差很少。html

1. 帶上幾個問題看源碼

1. promise鏈是如何實現的?
2. promise對象如何變成fulfill狀態,並觸發promise鏈條的後續函數?new Promise和Promise.resolve() 有何不一樣?
*3. 爲何每執行.then就建立一個新的Promise對象,而不能使用第一個promise依次.then?
4. 如何對throw Error 和 reject進行加工java

第3個問題不是在使用Promise的過程當中提出的問題,而是看源碼過程當中針對源碼實現提出的問題。node

分兩條主線來說解:
第3節:回調函數的設置、promise鏈條的保存
第4節:promise對象的解決(設置爲rejected、fulfilled)、鏈條的遷移es6



2. promise對象的狀態

咱們知道promise對象有Pendding、Fulfilled、Rejected三種狀態。Fulfilled和Rejected都屬於Settled。
在Promise對象內部,是經過this._bitField屬性的不一樣位來保存狀態信息的。
在Promise內部實現中,遠不止使用它時僅有的三種狀態,_bitField保存了不少其餘的狀態信息。好比Following、Followed、Migrated等等。算法

看一下內部置位、以及判斷是否置位等的函數。實際上都是位操做。數組

Promise.prototype._length = function () {
    return this._bitField & 131071;
};

Promise.prototype._isFollowingOrFulfilledOrRejected = function () {
    return (this._bitField & 939524096) > 0;
};

Promise.prototype._isFollowing = function () {
    return (this._bitField & 536870912) === 536870912;
};

Promise.prototype._setLength = function (len) {
    this._bitField = (this._bitField & -131072) |
        (len & 131071);
};

Promise.prototype._setFulfilled = function () {
    this._bitField = this._bitField | 268435456;
};

Promise.prototype._setRejected = function () {
    this._bitField = this._bitField | 134217728;
};

Promise.prototype._setFollowing = function () {
    this._bitField = this._bitField | 536870912;
};

Promise.prototype._setIsFinal = function () {
    this._bitField = this._bitField | 33554432;
};

Promise.prototype._isFinal = function () {
    return (this._bitField & 33554432) > 0;
};

Promise.prototype._cancellable = function () {
    return (this._bitField & 67108864) > 0;
};

Promise.prototype._setCancellable = function () {
    this._bitField = this._bitField | 67108864;
};

Promise.prototype._unsetCancellable = function () {
    this._bitField = this._bitField & (~67108864);
};

Promise.prototype._setIsMigrated = function () {
    this._bitField = this._bitField | 4194304;
};

Promise.prototype._unsetIsMigrated = function () {
    this._bitField = this._bitField & (~4194304);
};

Promise.prototype._isMigrated = function () {
    return (this._bitField & 4194304) > 0;
};



3. promise鏈如何實現 —— 註冊階段(.then)

咱們都知道設置一個promise鏈是經過promise對象的.then方法註冊fulfill、reject 狀態被激活時的回調函數。來看一下.then的代碼:promise


圖3-1

3.1 promise保存鏈條的結構

上圖能夠看到.then內部調用了._then,而後把咱們傳給.then()函數的didFulfill、didReject等回調函數經過_addCallbacks保存下來。這裏注意到,不是經過 「 this._addCallbacks() 」,而是經過 「 target._addCallbacks() 」,並且上一行還判斷了 「 target !== this 」的條件。那麼target是什麼呢?待會3.5節講。app

看到 _addCallbacks的實現,promise對象以每5個參數爲一組保存。當對一個promise對象調用一次.then(didFulfill, didReject)的時候,這組相關的參數保存在:異步

this._fulfillmentHandler0;  // promise對象被置爲fulfilled 時的回調函數
this._rejectionHandler0;  // promise對象被置爲rejected 時的回調函數。在3.3.1.1中知道,這個能夠用來保存followee
this._progressHandler0;
this._promise0;
this._receiver0;  // 當 fulfill被調用時  ,傳給函數的 this對象
代碼3-1

當在一個promise對象上超過一次調用.then(didFulfill, didReject) 時,大於1的部分以這種形式保存在promise對象上:

var base; // base表示每組參數的起點,每5個參數爲一組保存
this[base + 0];
this[base + 1];
this[base + 2];
this[base + 3];
this[base + 4];
代碼3-2


3.2 鏈條的拓撲結構 —— 爲什麼每一個.then 都new一個新的Promise對象?

不少說明文檔會給出這樣的示例代碼:

// 來自 http://liubin.org/promises-book/#ch2-promise.then
// promise能夠寫成方法鏈的形式

aPromise.then(function taskA(value){
// task A
}).then(function taskB(vaue){
// task B
}).catch(function onRejected(error){
    console.log(error);
});
代碼3-3

這樣的實現的任務塊是這樣一種拓撲結構:


圖3-2

而對於另外一種拓撲結構的任務,有all 和 race方法:


圖3-3

若是沒有深究,咋一看可能覺得上面的「代碼3-3」中,依次.then都是在同一個aPromise對象上,而.then所註冊的多個回調函數都保存在aPromise上。

事實上,看到上面圖3-1中,Promise.prototype._then的代碼裏面,每次執行_then都會新建一個Promise對象,好比代碼3-3實際上等效於這樣:

var bPromise = aPromise.then(function taskA(value){
// task A
});
var cPromise = bPromise.then(function taskB(vaue){
// task B
}).catch(function onRejected(error){
    console.log(error);
});
代碼3-4

aPromise、bPromise、cPromise分別是不一樣的對象。

那麼爲何這麼實現呢?想一下就會知道這樣多種拓撲結構:


圖3-4

當在同一個promise對象上屢次執行.then時,跟代碼3-3依次.then的狀況並不同,以下的示例代碼:

var bPromise = aPromise.then(function taskA(value){
  // task A
    return new Promise(function (resolve) {
        setTimeout(function () {
            return resolve();
        }, 5000);
    });
});
var cPromise = aPromise.then(function taskB(vaue){
  // task B
  console.log('task B');
});
代碼3-5

這裏用aPromise.then兩次,註冊兩個onFulfill函數(function taskA 和 function taskB)。當task A 裏返回新建的promise對象處於pending狀態時,task B的任務會先執行。

那麼這樣的promise鏈條是至關靈活的,能夠實現任何網狀的依賴關係。那麼經過這個發現,我想到利用它來作一件有趣的事情,能夠求有向圖最短路徑的值,看3.3節。


3.3 利用promise的拓撲特性作有趣的事 —— 有向圖的最短路徑另類求值


圖3-5

如上這個有向圖,要求0到3的最短路徑,那麼你可能第一想到的是Dijkstra算法、Floyd算法等等。

那麼利用promise在3.2節中講的特性,恰好能夠用來求最短路徑的值。但這裏只是求值(玩玩),不能代替「最短路徑算法」。上代碼:

1 var Promise = require('bluebird');
 2 
 3 var _base = 10;  // 等待時間基數
 4 
 5 var dot0 = new Promise(function (resolve) {
 6     return resolve('0');
 7 });
 8 
 9 var dot0_2 = dot0.then(function () {
10     return new Promise(function (resolve) {
11         setTimeout(function() {
12             return resolve('0');
13         }, 5 * _base);
14     });
15 });
16 
17 var dot0_3 = dot0.then(function () {
18     return new Promise(function(resolve) {
19         setTimeout(function () {
20             return resolve('0');
21         }, 30 * _base);
22     });
23 });
24 
25 var dot2 = Promise.race([dot0_2]);
26 
27 var dot2_1 = dot2.then(function (which) {
28     return new Promise(function (resolve) {
29         setTimeout(function () {
30             return resolve(which + ' 2');
31         }, 15 * _base);
32     });
33 });
34 
35 var dot2_5 = dot2.then(function (which) {
36     return new Promise(function (resolve) {
37         setTimeout(function () {
38             return resolve(which + ' 2');
39         }, 7 * _base);
40     });
41 });
42 
43 var dot5 = Promise.race([dot2_5]);
44 
45 var dot5_3 = dot5.then(function (which) {
46     return new Promise(function (resolve) {
47         setTimeout(function () {
48             return resolve(which + ' 5');
49         }, 10 * _base);
50     });
51 });
52 
53 var dot5_4 = dot5.then(function (which) {
54     return new Promise(function (resolve) {
55         setTimeout(function () {
56             return resolve(which + ' 5');
57         }, 18 * _base);
58     });
59 });
60 
61 var dot1 = Promise.race([dot2_1]);
62 
63 var dot1_4 = dot1.then(function (which) {
64     return new Promise(function (resolve) {
65         setTimeout(function () {
66             return resolve(which + ' 1');
67         }, 8 * _base);
68     });
69 });
70 
71 var dot4 = Promise.race([dot1_4, dot5_4]);
72 
73 var dot4_3 = dot4.then(function (which) {
74     return new Promise(function (resolve) {
75         setTimeout(function () {
76             return resolve(which + ' 4');
77         }, 4 * _base);
78     });
79 });
80 
81 var dot3 = Promise.race([dot0_3, dot4_3, dot5_3])
82     .then(function (str) {
83         console.log('result: ', str + ' 3');
84     });
代碼3-6

// 輸出結果:
// 0 2 5 3

若是咱們把2->1邊的權值改爲4,即把第31行代碼的15改爲4,那麼輸出結果會是 : 0 2 1 4 3

換種寫法(結果同樣):

1 var Promise = require('bluebird');
 2 
 3 var _base = 10;
 4 // key表示頂點,值表示出邊
 5 var digram = {
 6     '0': { '2': 5, '3': 30 },
 7     '2': { '1': 15, '5': 7 },
 8     '5': { '3': 10, '4': 18 },
 9     '1': { '0': 2, '4': 8 },
10     '4': { '3': 4 },
11     '3': {}
12 };
13 var order = ['0', '2', '5', '1', '4', '3'];
14 var startDot = '0';
15 var endDot = '3';
16 
17 var promiseMap = {};
18 function _buildMap() {
19     for(var dot in digram)
20         promiseMap[dot] = {_promise: undefined, _in: [], _out: []};
21     for(var i = 0 ; i < order.length; ++i) {    // 這裏不能用 for(var dot in digram),由於js對map的key會排序,這樣取出來的dot順序是0、一、二、三、四、5
22         var dot = order[i];
23         if (dot == startDot) {
24             promiseMap[dot]._promise = Promise.resolve();
25         } else if (dot == endDot) {
26             var localdot = dot;
27             promiseMap[dot]._promise = Promise.race(promiseMap[dot]._in)
28                 .then(function (str) {
29                     console.log('result: ', str + ' ' + localdot);
30                 });
31             continue;
32         } else {
33         debugger;
34             promiseMap[dot]._promise = Promise.race(promiseMap[dot]._in);
35         }
36         for(var edge in digram[dot]) {
37             var edgePromise = 
38                 promiseMap[dot]._promise.then(function (which) {
39                     var self = this;
40                     return new Promise(function (resolve) {
41                         setTimeout(function () {
42                             return resolve( (which ? which + ' ' : '') + self.dot);
43                         }, digram[self.dot][self.edge] * _base);    // 這裏不能直接訪問外層dot、edge,由於異步函數被調用的時候值已經被改變,也沒法經過for循環裏面保存tmpdot、tmpedge的辦法,由於js沒有塊級做用域,es6新標準有塊級做用域
44                     });
45                 }.bind({dot: dot, edge: edge}));
46             promiseMap[dot]._out.push(edgePromise);
47             promiseMap[edge]._in.push(edgePromise);
48         }
49     }
50 }
51 _buildMap();
代碼3-7

// 輸出結果:

// 0 2 5 3


3.4 .then鏈條的結構

那麼經過3.一、3.2節的理解,咱們知道了,一個.then鏈條裏面的結構並非這樣:


圖3-6

這是在同一個promise對象上屢次.then的狀況(代碼3-5)。

而依次.then的鏈條(代碼3-3 / 代碼3-4)是這樣的:


圖3-7

就是說若是這樣的代碼,不使用同一個promise對象,去.then兩次,那麼3.1中_addCallbacks的結構只會用到【this._promise0、】這一組,而不會有【this[base + index]】這些數據。


3.5 Promise.prototype._target()

3.1節留了一個疑問,在調用promise.then註冊一個回調函數的時候,不是經過「 this._addCallbacks() 」 而是經過 「target._addCallbacks() 」,那麼這個target是什麼?
經過上幾節,瞭解了內部鏈條保存的細節,如今來看一下target。

看個示例代碼:


圖3-8

那麼經過app2.js,能夠看到通常狀況下,aPromise._target() 取到的target是this對象。經過target(aPromise)調用_addCallbacks時,bPromise是存在aPromise._promise0裏面的。
經過app3.js,能夠發現,當對aPromise使用一個pending狀態的cPromise對象進行resolve時,aPromise._target()取到的target會變成cPromise,後續經過aPromise.then所建立的bPromise對象也都是經過target(cPromise)進行_addCallbacks的,這個時候aPromise._promise0就是undefined,而cPromise._promise0就是bPromise。

那麼這裏target的變更與promise鏈條的遷移如何實現呢?這裏涉及到解決(settle)一個promise對象的細節,第4.3.1.1節會再講到。



4. promise對象的resolve細節 —— 解決階段(.resolve)

4.1 resolve一個promise對象的幾種狀況

看下示例代碼:

var Promise = require('bluebird');

var aPromise = new Promise(function (resolve) {
    return resolve();  // resolve的調用可能在任何異步回調函數裏面
});

var bPromise = aPromise.then(function () {
    var dPromise = Promise.resolve();
    return dPromise;
});

var cPromise = bPromise.then(function () {
    console.log('cPromise was resolved');
});
代碼4-1
  1. 構造函數的回調函數裏面,經過resolve()由咱們手動觸發解決,例如上面的 aPromise。resolve可能在任何異步回調函數裏面被調用。
  2. 經過Promise.resolve()建立一個已經被解決的promise對象
  3. then函數註冊的回調函數,會在上游promise對象被解決掉以後,由promise的機制觸發後續promise對象被解決。好比aPromise被resolve以後,bPromise、cPromise 由Promise的機制進行解決。

這幾種狀況的細節在4.3節講。


4.2 Promise的隊列管理對象 —— async

async是Promise用來管理promise鏈中全部promise對象的settle 的一個單例對象,在async.js文件:


圖4-1

async提供兩個接口:

  1. settlePromises:接收一個promise對象,針對4.1節中的狀況1,調用async.settlePromises去把要settle的promise對象入隊
  2. invoke:接收一個回調函數的相關參數,針對4.1節中的狀況2,把被settle的上游promise中保存的回調函數(3.1節中的參數組)經過async.invoke,把要執行的回調函數經過this._schedule()去執行

關於this._schedule(),視條件可能有不少種狀況,可能不用異步,可能經過process.nextTick(),可能經過setTimeout(fn, 0),可能經過setImmediate(fn) 等等。
schedule的各類實如今schedule.js文件。
在4.3.2講解的例子中,就是經過process.nextTick()實現的。


4.3 resolve一個promise鏈的細節

針對4.1節講的幾種狀況,進行詳細說明。

4.3.1 構造函數裏的resolver

來看代碼:


圖4-2

看右上角的示例代碼,右下角是輸出結果,爲何「step 2」不是在「step 1」以前呢?能夠知道構造一個Promise對象時,傳進去的函數(也即源碼裏面的resolver)是被同步執行的(若是是異步執行的,那麼「step 1」一定在「step 2」以後),這也意味着,若是在該回調函數裏面同步調用resolve(),那麼該Promise對象被建立以後就已是fulfilled狀態了。【看step 2 的輸出】。
能夠從左邊的源碼看到,傳給構造函數的回調函數是被同步執行的。
能夠看出構造函數的參數 —— resolver回調函數「step 1」被調用的時機。

那段代碼有點繞,能夠來剖析一下。

Promise.prototype._resolveFromResolver = function (resolver) {
...
    var r = tryCatch(resolver)(function(value) {
        if (promise === null) return;
        promise._resolveCallback(value);
        promise = null;
    }, function (reason) {
        if (promise === null) return;
        promise._rejectCallback(reason, synchronous);
        promise = null;
    });
...
};
代碼4-2

這裏的tryCatch()暫時忽略它,下面講捕獲異常時講到。這裏徹底能夠當作:

var r = resolver(function(value) {
        if (promise === null) return;
        promise._resolveCallback(value);
        promise = null;
    }, function (reason) {
        if (promise === null) return;
        promise._rejectCallback(reason, synchronous);
        promise = null;
    });
代碼4-3

resolver就是咱們new Promise時傳進去的回調函數:

var aPromise = new Promise(function (resolve) {
  console.log('step 1');
  return resolve();
});
代碼4-4

而咱們傳進去的回調函數的resolve參數,是bluebird調用resolver時傳出來給咱們的回調函數:

function(value) {
  if (promise === null) return;
  promise._resolveCallback(value);
  promise = null;
}
代碼4-5

同理,代碼4-3中那個function (reason) {} 也即平時new Promise(function (resolve, reject) {}) 時傳出來的reject函數。

這樣,當咱們new Promise時,在傳進去的resolver裏面調用resolve()時(不論是同步仍是在異步回調函數裏面),實際上就是調用了代碼4-5這個函數。
而value就是咱們調用resolve()時傳進去的解決值,這個值能夠被傳遞給.then()註冊的回調函數的參數。
因此resolve()實際上調用的是promise._resolveCallback(value)。在這個函數裏面,去修改當前promise對象的狀態爲fulfilled。

4.3.1.1 promise._resolveCallback() 與value

在3.5中,aPromise的resolver裏面,最終是經過resolve(cPromise) 去解決aPromise的,而這個cPromise是一個處於pending狀態的promise對象。
而後就說到aPromise._target() 變成了cPromise。而且後續經過aPromise.then()註冊進去的鏈條都掛在cPromise對象上。
那麼resolve(cPromise)實際上就是aPromise._resolveCallback(value)中的value=cPromise。

Promise.prototype._resolveCallback = function(value, shouldBind) {
    if (this._isFollowingOrFulfilledOrRejected()) return;
    if (value === this)
        return this._rejectCallback(makeSelfResolutionError(), false, true);
    var maybePromise = tryConvertToPromise(value, this);
    if (!(maybePromise instanceof Promise)) return this._fulfill(value);

    var propagationFlags = 1 | (shouldBind ? 4 : 0);
    this._propagateFrom(maybePromise, propagationFlags);
    var promise = maybePromise._target();
    if (promise._isPending()) {
        var len = this._length();
        for (var i = 0; i < len; ++i) {
            promise._migrateCallbacks(this, i);
        }
        this._setFollowing();
        this._setLength(0);
        this._setFollowee(promise);
    } else if (promise._isFulfilled()) {
        this._fulfillUnchecked(promise._value());
    } else {
        this._rejectUnchecked(promise._reason(),
            promise._getCarriedStackTrace());
    }
};
...
Promise.prototype._fulfill = function (value) {
    if (this._isFollowingOrFulfilledOrRejected()) return;
    this._fulfillUnchecked(value);
};
...
Promise.prototype._fulfillUnchecked = function (value) {
    if (value === this) {
        var err = makeSelfResolutionError();
        this._attachExtraTrace(err);
        return this._rejectUnchecked(err, undefined);
    }
    this._setFulfilled();
    this._settledValue = value;
    this._cleanValues();

    if (this._length() > 0) {
        this._queueSettlePromises();
    }
};
代碼4-6

能夠看到當value不是Promise時,直接return this._fulfill(value)。而且最終在_fulfillUnchecked()裏面_setFulfilled(),這是第二節的那些狀態設置和檢驗函數。
當value是pending狀態的Promise時,就會把當前的aPromise _setFollowing(),而且_setFollowee(cPromise)。(實際上這裏也並不必定是cPromise,若是cPromise還有其餘followee的話,這裏是先經過cPromise._target()取出來的cPromise所跟隨的最終promise對象。)
_setFollowing()也是第二節的狀態設置函數。_setFollowee()就是給當前promise對象設置一個跟隨對象。
看下面代碼,實際上就是this._rejectionHandler0。
而注意到this._target()函數,事實上不是返回一個屬性,而是判斷當前的promise是否是被設置成「following」狀態了,是的話返回「跟隨對象」,一直循環到最終那個promise。

Promise.prototype._target = function() {
    var ret = this;
    while (ret._isFollowing()) ret = ret._followee();
    return ret;
};

Promise.prototype._followee = function() {
    return this._rejectionHandler0;
};

Promise.prototype._setFollowee = function(promise) {
    this._rejectionHandler0 = promise;
};
代碼4-7

再次看回_resolveCallback()的實現,當value是pending狀態的promise時,在給aPromise設置following狀態而且設置與cPromise的跟隨關係以前,還有一個cPromise._migrateCallbacks(aPromise, i)的過程。
這migrate的就是3.1中講的屢次.then()時保存的那對參數組,其中第四個參數是.then()時建立的promise。
如今follower(即aPromise)上.then()的後續參數組都被遷移到followee(即cPromise)上面。
並且這些被遷移的參數組中的第四個參數被_setIsMigrated()。

Promise.prototype._migrateCallbacks = function (follower, index) {
    var fulfill = follower._fulfillmentHandlerAt(index);
    var reject = follower._rejectionHandlerAt(index);
    var progress = follower._progressHandlerAt(index);
    var promise = follower._promiseAt(index);
    var receiver = follower._receiverAt(index);
    if (promise instanceof Promise) promise._setIsMigrated();
    this._addCallbacks(fulfill, reject, progress, promise, receiver, null);
};
代碼4-8

4.3.2 .then註冊的回調函數被觸發的機制 —— aPromise.then時,已經是fulfilled狀態

那再來看上圖4-2的示例代碼中,經過aPromise.then()建立的bPromise對象。
咱們知道aPromise 變成fulfilled以後,經過aPromise.then註冊的bPromise也是會被settle的。而在aPromise.then的時候,aPromise自己已是fulfilled狀態的。那麼經過「step 3」的輸出、以及「step 3」和「step 4」的順序,能夠知道經過.then()建立的promise對象的onFulfilled函數是被異步執行的(無論.then的時候aPromise是否fulfilled),並且經過「step 5」的輸出,咱們能夠猜到這個異步大體也是經過process.nextTick() 處理的。

在圖3-1中知道aPromise.then()最終調用了async.invoke(target._settlePromiseAtPostResolution, target, callbackIndex);
這個callbackIndex就是promise鏈條的index。
而從4.2中,知道async.invoke()最終致使了該回調函數被經過process.nextTick()異步執行了。
同時能夠知道,step 5和step 4的順序是不必定的,由於經過setTimeout、setImmediate都不同。並且不一樣版本的node,這幾個函數的執行順序也不必定同樣。

4.3.3 .then註冊的回調函數被觸發的機制 —— aPromise.then時,處於pending狀態

4.3.2中講了aPromise爲已經fulfilled時,.then產生的後續promise對象在 async.invoke(target._settlePromiseAtPostResolution, target, callbackIndex)中經過process.nextTick進行settle。
那麼aPromise.then產生bPromise時,aPromise仍是pending狀態,這時後續的bPromise對象的settle要等到aPromise被手動resolve()時再觸發。
在代碼4-6中,知道aPromise對象被經過resolve(value) settle掉時,最終調用_fulfillUnchecked()。
裏面再調用了this._queueSettlePromises()。在這裏面,把後續的promise對象一一解決。

Promise.prototype._queueSettlePromises = function() {
    async.settlePromises(this);
    this._setSettlePromisesQueued();
};
代碼4-9

一樣是經過async來管理。

Async.prototype.settlePromises = function(promise) {
    if (this._trampolineEnabled) {
        AsyncSettlePromises.call(this, promise);
    } else {
        this._schedule(function() {
            promise._settlePromises();
        });
    }
};
代碼4-10

4.3.4 .then註冊的回調函數被觸發的機制 —— bPromise.then時,bPromise自己就是.then產生的一個promise對象

在4.3.3 中解決了bPromise時,在async.settlePromises()裏面又反過來調用bPromise._settlePromises()。這會激發bPromise解決後續鏈條。

Promise.prototype._settlePromises = function () {
    this._unsetSettlePromisesQueued();
    var len = this._length();
    for (var i = 0; i < len; i++) {
        this._settlePromiseAt(i);
    }
};
代碼4-11

4.3.5 總結.then鏈條的解決

那麼結合4.3.1 - 4.3.4,咱們看這樣一個promise鏈的解決時機是怎樣的,示例代碼:

var aPromise = new Promise(function (resolve) {
    return resolve();
})
.then(function () {        // 假設這裏建立的是bPromise
   // task B 
})
.then(function () {        // 假設這裏建立的是cPromise
    // task C
});
代碼4-12

解決順序:

  1. aPromise建立之時,同步執行了構造函數的回調函數,同步執行了resolve。這個是4.3.1節的狀況。

  2. bPromise在建立的時候,aPromise已經爲fulfilled狀態,這時經過async.invoke(target._settlePromiseAtPostResolution, target, callbackIndex),把bPromise的settle任務放到process.nextTick。這個是4.3.2節的狀況。

  3. cPromise在建立的時候,注意這裏cPromise不是經過aPromise.then產生的,而是bPromise.then產生的,那麼這個時候bPromise仍是pending狀態的,因此cPromise的settle任務是4.3.5節裏面的狀況。



5. tryCatch 處理

在4.3.1中暫時忽略了tryCatch(),如今來看看實現。
在util.js文件:

var errorObj = {e: {}};
var tryCatchTarget;
function tryCatcher() {
    try {
        var target = tryCatchTarget;
        tryCatchTarget = null;
        return target.apply(this, arguments);
    } catch (e) {
        errorObj.e = e;
        return errorObj;
    }
}
function tryCatch(fn) {
    tryCatchTarget = fn;
    return tryCatcher;
}
代碼5-1

因此在下面這段代碼裏面

Promise.prototype._resolveFromResolver = function (resolver) {
...
    var r = tryCatch(resolver)(function(value) {
        if (promise === null) return;
        promise._resolveCallback(value);
        promise = null;
    }, function (reason) {
        if (promise === null) return;
        promise._rejectCallback(reason, synchronous);
        promise = null;
    });
...
};

實際上tryCatch只是轉換了一下error的形勢。把throw 出來的error,變成了return 回來的一個自定義的errorObj。
這樣,若是你沒有捕獲異常,這裏面的異常也不會變成node的未捕獲異常,而是bluebird的內部機制幫你捕獲了。
而若是你沒有在promise鏈條的末端catch(),那麼bluebird幫你捕獲的未解決異常最終會輸出。
看下面示例代碼。

var aPromise = new Promise((resolve, reject) => {
  console.log(aaa.abc);
});

輸出:
Unhandled rejection ReferenceError: abc is not defined
    at ..../app.js:7:15
    at tryCatcher (..../node_modules/bluebird/js/main/util.js:26:23)
    at .......

若是手動.catch()再輸出:

var aPromise = new Promise((resolve, reject) => {
  console.log(aaa.abc);
}).catch((err) =>  {
  console.error(err);
});

輸出:
ReferenceError: abc is not defined
    at app.js:7:15
    at tryCatcher (/...../node_modules/bluebird/js/main/util.js:26:23)

在resolver裏面返回error,也能夠經過return reject(err); 返回的異常也會出如今Promise鏈條中。

相關文章
相關標籤/搜索