• Promise/Bluebird源码


    本作品采用知识共享署名 4.0 国际许可协议进行许可。转载联系作者并保留声明头部与原文链接https://luzeshu.com/blog/bluebirdsource
    本博客同步在http://www.cnblogs.com/papertree/p/7163870.html


    时隔一年,把之前结尾还有一部分未完成的博客完成。版本2.9。具体忘了哪个revision number。不过原理差不多。

    1. 带上几个问题看源码

    1. promise链是如何实现的?
    2. promise对象如何变成fulfill状态,并触发promise链条的后续函数?new Promise和Promise.resolve() 有何不同?
    *3. 为什么每执行.then就创建一个新的Promise对象,而不能使用第一个promise依次.then?
    4. 如何对throw Error 和 reject进行加工

    第3个问题不是在使用Promise的过程中提出的问题,而是看源码过程中针对源码实现提出的问题。

    分两条主线来讲解:
    第3节:回调函数的设置、promise链条的保存
    第4节:promise对象的解决(设置为rejected、fulfilled)、链条的迁移



    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的代码:

    图3-1

    3.1 promise保存链条的结构

    上图可以看到.then内部调用了._then,然后把我们传给.then()函数的didFulfill、didReject等回调函数通过_addCallbacks保存下来。这里注意到,不是通过 “ this._addCallbacks() ”,而是通过 “ target._addCallbacks() ”,而且上一行还判断了 “ target !== this ”的条件。那么target是什么呢?待会3.5节讲。

    看到 _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、1、2、3、4、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.1、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链条中。

  • 相关阅读:
    py 5.11
    py 5.10
    py 5.9
    py 5.8
    python 5.7
    python 5.4
    python 5.3
    python 5.2
    python 4.28
    python 4.27
  • 原文地址:https://www.cnblogs.com/papertree/p/7163870.html
Copyright © 2020-2023  润新知