源码参考链接:https://github.com/stefanpenner/es6-promise

完整的 promise.js 链接

构造函数 Promise

  1. 三个状态: PENDING, FULFILL, REJECT

  2. PID 记录 promise id 属性

  3. _state 当前 promise 的状态(pending/fulfill/reject)

  4. _result 当前 promise 任务执行的结果值

  5. _subs 当前 promise 的订阅者(then 时注册的 resolver/rejection)

  6. 构造函数中立即执行 resolver 根据任务执行情况由使用者决定是调用 resolvePromise 还是 rejectPromise

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  var PID = Math.random().toString(36).substring(2);
  var PENDING = 0;
  var FULFILL = 1;
  var REJECT = 2;
  var i = 0;
  var proto = MyPromise.prototype;
  var noop = function () {};

  function MyPromise(resolver) {
    this[PID] = i++;
    this._state = PENDING;
    this._result = undefined;
    this._subs = [];

    if (!this instanceof Promise) {
      throw new TypeError("只能通过new 构造 Promise 实例。");
    }

    var _this = this;
    try {
      resolver(
        function resolvePromise(value) {
          resolve(_this, value);
        },
        function rejectPromise(reason) {
          reject(_this, reason);
        }
      );
    } catch (e) {
      reject(this, e);
    }
  }
  MyPromise.prototype.then = then;

  function resolve(val) {
    console.log(val, 'resolve')
  }
  function reject() {}

测试:

1
2
3
4
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)
  const p = new MyPromise(function (resolve, reject) {
    Util.delay(() => resolve(100), 1000)
  })
100 resolve

resolve 和 reject 函数

这两个函数至关重要,他们负责改变 promise 的状态值,也是整个 Promise 实现过程中唯 一能改变状态值的地方。

他们的执行会触发所有回调的执行(promise._subs)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
  function resolve(promise, value) {
    if (promise === value) {
      reject(promise, Util.error.returnSelfPromise());
    } else if (Util.isObjectOrFunction(value)) {
      // TODO
    } else {
      fulfill(promise, value);
    }
  }

  function reject(promise, reason) {
    if (promise._state !== PENDING) {
      return;
    }

    promise._state = REJECT;
    promise._result = reason;

    asap(function () {
      publish(promise);
    });
  }

asap 函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  // 将所有任务添加到一个队列,根据平台决定调用那个异步函数
  var queue = new Array(1000);
  var qlen = 0;
  function asap(callback) {
    queue[qlen] = callback;

    qlen++;

    if (qlen === 1) {
      // 这里是通过第一次入列操作来触发 flush 队列操作
      // 因为 flush 是异步执行,所以在它还没之前之前有可能有新的任务入列
      // 这个时候 qlen > 1 ,直到 flush 执行,通过 qlen 遍 queue 确保在执行的时刻
      // 可以将这之前的所有入列的任务都得到执行
      setTimeout(flush());
    }
  }

  function flush() {
    for (var i = 0; i < qlen; i++) {
      var callback = queue[i];
      if (callback) callback();

      // 清空已执行的任务
      queue[i] = undefined;
    }

    // 在此刻至 Flush之前入列的任务都得到了执行,重置重新接受新的任务
    qlen = 0;
  }

按照 Promise 的定义,被 Promise 定义的任务不论代码是异步的还是同步的,都会被当做 异步任务来执行,比如:

1
2
3
4
  new Promise((resolve)=> {
    resolve(100)
  }).then(val => console.log(100))
  console.log(200)
200
100

结果显示 200 先输出,后输出 100,但是其实我们在 new Promise() 的时候传入的函数 里面其实都是同步代码,经过 Promise 封装置后都成了异步的了。

因此这里的 asap 就是这个作用,当任务就算是同步代码的时候,依然将其变成异步任务去 执行。

并且这里使用了一个队列 queue 来管理这些任务,针对原作者的代码做了一些改动,去 掉了平台有关的代码,并将任务直接二次封装成了一个函数,所以这里是 qlen++ 而不是 qlen +=2

这里如果不仔细思考可能还不太好理解原作者为什么这么做???

  1. 为什么 qlen === 1 的时候触发 flush ?

    :其实想明白了也简单,就是为了只要队列是空的时候一旦有新的任务进来就立即触发任务 出列 flush 掉队列中所有的任务,并且是顺序执行,顺序执行,顺序执行,重要的事 情说三遍嘛,想象下如果没有这个机制,一旦有 promise settled 了,就调用一个 setTimeout ?

    有了这个机制之后,在 当前的 setTimeout flush 之前,会尽可能的让当前队列承载 更多的 promise 任务,直到 flush 结束,重启另一个 setTimeout。

  2. 为什么不直接使用 queue 数组的长度来控制 ?

    TODO

情景测试:在 flush 之前 qlen 值发生变化了,需要做点修改让效果更直观。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11

  function asap(callback) {
    queue[qlen] = callback;

    qlen++;

    if (qlen === 1) {
      // 这里加大 flush 的时间点,让 asap 有足够的时间来响应更多的异步任务
      setTimeout(flush, 3000);
    }
  }

测试代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p1 = new MyPromise((resolve) => {
    Util.delay(() => resolve(100))
  }).then(val => {
    console.log(val, 'p1 then 1')
  })

   new MyPromise((resolve) => {
    Util.delay(() => resolve(100))
  }).then(val => {
    console.log(val, 'p2 then 1')
  })

  new MyPromise((resolve) => {
    Util.delay(() => resolve(100))
  }).then(val => {
    console.log(val, 'p3 then 1')
  })

  new MyPromise((resolve) => {
    Util.delay(() => resolve(100))
  }).then(val => {
    console.log(val, 'p4 then 1')
  })
{ qlen: 1 }
{ qlen: 2 }
{ qlen: 3 }
{ qlen: 4 }
100 p1 then 1
100 p2 then 1
100 p3 then 1
100 p4 then 1

看到没, qlen=4 了,因为我们在 asap 中调用 flush 的时候加了 3 秒的延时,所以能 很直观的看得到在一个 settimeout 回调之前会接受到多个 promise 任务。

then 函数实现

then 功能说明:

  1. 收集 pending 状态 promise 的 callback(存放到 _subs 中)

    因为 promise 任务如果异步的,调用 then(resolve,reject) 的时候,resolve 和 reject 是不应该立即执行的,必须等异步任务结束之后再执行,否则就不符合了 promise 原则(异步任务同步化)。

    所以当 promise 任务是异步情况下,then 函数的功能应该是用来收集 resolve/reject 的,等待任务结束后调用。

  2. 作为 then 链式调用的桥梁,即这个桥梁必须是在这个函数里面去完成的。

既然有了收集,那必然就有触发动作,触发也必须等待任务执行完成才会触发,也就是说这 个动作必须是在 resolve() 里面完成,因为 Promise 使用者会根据自己任务情况去在适 当的位置调用 resolve 和 reject。

需要完成的函数:

  • fulfill(promise, value) ,任务成功完成

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    
      function fulfill(promise, result) {
        if (promise._state !== PENDING) {
          // 状态已经完成不能再改变状态
          return;
        }
    
        promise._state = FULFILL;
        promise._result = result;
    
        if (promise._subs.length > 0) {
          asap(function () {
            publish(promise);
          });
        }
      }
    
  • publish(promise)

    任务完成之后 flush 掉所有回调(then pending 阶段收集的 _subs[])

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    
      function publish(promise) {
        var subs = promise._subs;
    
        var child,
            callback,
            result = promise.result;
        for (var i = 0; i < subs.length; i += 3) {
          child = subs[i];
          callback = subs[i + promise._state];
    
          if (child) {
            // TODO 异步任务
          } else {
            callback(result);
          }
        }
    
        subs.length = 0;
      }
    
  • subscribe(parent, child, onFulfillment, onRejection)

    如果任务是个异步任务就不会立即执行,要等到任务结束才能执行回调,所以就必须要有 个地方能将这些回调收集到当前的 promise 实例中,等待调用。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    
      function subscribe(parent, child, onFulfillment, onRejection) {
        var subs = parent._subs;
        var len = subs.length;
        // PENDING
        subs[len] = child;
        subs[len + FULFILL] = onFulfillment;
        subs[len + REJECT] = onRejection;
    
        // parent promise 状态如果完成了,立即触发当前 child 的 promise
        // 可能执行到这里的时候任务刚好完成了???
        if (len === 0 && parent._state) {
          asap(function () {
            publish(parent);
          });
        }
      }
    
  • then(onFulfillment, onRejection)

    这里要区分两种情况,一种是 pending 状态和非 pending 状态的处理,pending 说明可 能是异步任务还没结束,不能立即 settled,调用 subscribe() 去收集回调。

    一种是非 pending 状态,在调用 then 之后只有一种情况会使得 promise 状态改变了, 那就是任务立即执行,调用了 resolvereject 设置了 promise._state 改变 了状态,因为只有这两个函数才会改变 promise 状态值。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    
      function then(onFulfillment, onRejection) {
        var parent = this;
        // 创建一个新的 promise,用来衔接后面的 then
        var child = new this.constructor(noop);
        var _state = this._state;
        // 根据状态决定执行哪个回调
        var callback = arguments[_state - 1];
    
        if (_state) {
          // 状态已经改变,任务已经完成了,直接执行回调
          invokeCallback(_state, child, callback, parent._result);
        } else {
          // 订阅所有回调
          subscribe(parent, child, onFulfillment, onRejection);
        }
    
        return child;
      }
    
  • invokeCallback(settled, promise, callback, detail)

    这个函数承载了当前 then promise1 的回调执行并解析结果(异常处理),然后将值传递 给下一个 then promise2(then 里面 new this.constructor(noop) 出来的),调用 resolve(child)~或 ~reject(child) 去触发 promise2 的回调。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    
      function invokeCallback(settled, promise, callback, detail) {
        var value; // 记录 callback 执行的结果
        var hasCallback = typeof callback === "function";
        var succeeded = true; // callback 可能执行失败
        var error;
    
        if (hasCallback) {
          // 开始执行 callback, 即 then(resolve, reject) 的 Resolve/Reject
          try {
            // 将上一个 promise 结果作为参数传递到 then 回调
            value = callback(detail);
          } catch (e) {
            // 回调执行失败,有错误或者异常
            error = e;
            succeeded = false;
          }
    
          if (promise === value) {
            reject(promise, Util.error.returnSelfPromise());
            return;
          }
        } else {
          // 没有回调的时候 then() ???
          value = detail;
        }
    
        // 这里要检测下一个新 new 的 promise 状态
        // 下面的动作都是为了下一个 then 做准备的,这里的promise
        // 是在上一个 then 里面的new 出来的 promise 衔接下一个 then 用
        if (promise._state !== PENDING) {
          // noop 状态完成了的 promise
        } else if (hasCallback && succeeded) {
          // 执行成功, resolve
          resolve(promise, value);
        } else if (succeeded === false) {
          // then 中的回调执行失败了
          reject(promise, error);
        } else if (settled === FULFILL) {
          fulfill(promise, value);
        } else if (settled === REJECT) {
          reject(promise, value);
        }
      }
    

测试:

1
2
3
4
5
6
7
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p = new MyPromise((resolve, reject) => {
    Util.delay(() => resolve(100))
  }).then(val => {
    Util.log(val, 'then 1 resolve')
  })

+RESULTS 实现 invokeCallback 之前:

undefined

这里没任何输出,因为还没实现 invokeCallback(settled, promise, callback, detail) 这里面会针对 then 的 resolve 或 reject 执行结果做出相应的处理。

实现关键点:

  1. callback 实际上是 then(resolve, reject) 中的 resolve/reject ,根据上一个 promise 状态 settled 决定的。

  2. 使用 try…catch 捕获 callback 执行异常,确保 then 回调也能受 Promise 规则约 束。

  3. 几种情况决定调用 resolve 还是 reject 进入下一个链式回调(then)。

+RESULTS 实现 invokeCallback 之后:

100 then 1 resolve

此时的 promise._subs 如下:

[
  MyPromise {
    '8st4da5md17': 1,
    _state: 0,
    _result: undefined,
    _subs: [] // 这是那个 child promise
  },
  [Function (anonymous)], // 这里是 then resolver
  undefined // 这里是 then rejection 因为没传所以是 undefined
]

then 链式调用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p = new MyPromise((resolve, reject) => {
    Util.delay(() => resolve(100))
  }).then(val => { /* p1 */
    Util.log(val, 'then 1 resolve')
    return 200
  }, /* p2 */).then(val => {/* p3 */
    Util.log(val, 'then 2 resolve')
    return 300
  }, /* p4 */)
100 then 1 resolve
200 then 2 resolve

then callback 返回对象或函数

针对返回对象的情况,其实也可以跟普通类型处理一样:

首先要把 Resolve 里面对对象和函数的检测去掉,或者也让它执行 fulfill(promise, value) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  function resolve(promise, value) {
    if (promise === value) {
      reject(promise, Util.error.returnSelfPromise());
    } else if (Util.isObjectOrFunction(value)) {
      // TODO
      fulfill(promise, value);
    } else {
      fulfill(promise, value);
    }
  }

实例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p = new MyPromise((resolve) => {
    Util.delay(() => resolve(100))
  }).then(val => {
    console.log(val, ', then 1')
    return { name: 'then 1 return' }
  }).then(val => {
    console.log(val, ', then 2')
  })
100 , then 1
{ name: 'then 1 return' } , then 2

但是如果有多个 then 链式调用的情况,一般都会返回一个对象,并且常见情况会是一个异 步的 promise ,这样统一当成普通类型处理就显得不太合理了,这也是为何原作者将返回 值是 函数或对象 的时候分开处理了。

因为在实际使用中,有以下几种场景:

  • 返回纯对象类型(非 promise 或 带有 then 的对象)

  • 返回一个新的 promise 实例

  • 返回一个带有 then 的对象或函数

所以需要做一些特殊处理。

value 可能是 null

捕获这种情况异常执行 reject。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19

  function resolve(promise, value) {
    if (promise === value) {
      reject(promise, Util.error.returnSelfPromise());
    } else if (Util.isObjectOrFunction(value)) {
      let then;

      try {
        then = value.then;
      } catch (e) {
        // value 可能是 undefined 或 null,或其他非法类型(如:数字)
        reject(promise, e);
        return;
      }
      // handleMaybeThenable(promise, value, then);
    } else {
      fulfill(promise, value);
    }
  }

测试:

1
2
3
4
5
6
7
8
9
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p = new MyPromise(resolve => {
    Util.delay(resolve(null))
  }).then(val => {
    console.log(val, 'p then resolve 1')
  }, reason => {
    console.log(reason.message, ', p then reject 1')
  })
Cannot read property 'then' of null , p then reject 1

value 返回的是一个 Promise 实例

即使用者在 then 回调里面 new 了一个 Promise 实例返回出来,这也是常见的使用场 景之一,经常会有多个有依赖前后结果的异步请求的时候,通过 promise then 来链 式同步执行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19

  function resolve(promise, value) {
    if (promise === value) {
      reject(promise, Util.error.returnSelfPromise());
    } else if (Util.isObjectOrFunction(value)) {
      let then;

      try {
        then = value.then;
      } catch (e) {
        // value 可能是 undefined 或 null,或其他非法类型(如:数字)
        reject(promise, e);
        return;
      }
      handleMaybeThenable(promise, value, then);
    } else {
      fulfill(promise, value);
    }
  }

增加 handleMaybeThenable(promise, value, then); 函数处理其他情况。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
  function handleMaybeThenable(promise, thenable, then) {

    // originalThen 实现的 then 函数,即 MyPromise.prototype.then
    // 这能确保 thenable 的确是我们的 MyPromise 实例
    if (thenable.constructor === promise.constructor && thenable.resolve === originalResolve && thenable.then === originalThen) {
      // 这里要做的处理是直接针对 thenable 状态做出判断
      if (thenable._state === FULFILL) {
        fulfill(promise, thenable._result)
      } else if (thenable._state === REJECT) {
        reject(promise, thenable._result)
      } else {
        // 直接构造 resolver 和 rejection
        subscribe(thenable, undefined, val => resolve(promise, val), reason => reject(promise, reason))
      }
    } else {
      // TODO
    }
  }

这样,我们就可以处理 then 回调中返回一个 Promise 实例的情况了。

测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p = new MyPromise(resolve => {
    Util.delay(resolve(100))
  }).then(val => {
    console.log(val, ', p then 1 resolve')
    return new MyPromise(resolve => {
      Util.delay(resolve(200))
    })
  }).then(val => {
    console.log(val, ', p then 2 resolve')
  })
100 , p then 1 resolve
200 , p then 2 resolve
undefined

所以很顺利的就看到了正确的结果,因为返回的本身就是 MyPromise ,所以只需要根据 其状态做相应的处理即可(fulfill / reject / subscribe)。

value 有可能有自己的 then 函数呢?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  function handleForeignThenable(promise, thenable, then) {
    asap(() => () => {
      let sealed = false; // 保证只会执行一次
      try {
        then.call(
          thenable,
          (value) => {
            if (sealed) return;
            sealed = true;

            if (value !== thenable) {
              resolve(promise, value);
            } else {
              fulfill(promise, value);
            }
          },
          (reason) => {
            if (sealed) return;
            sealed = true;

            reject(promise, reason);
          }
        );
      } catch (e) {
        sealed = true;
        reject(promise, e);
      }
    });
  }

测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p = new MyPromise(resolve => {
    Util.delay(resolve(100))
  }).then(val => {
    console.log(val, ', p then 1 resolve')

    return {
      name: 1,
      then(resolve, reject) {
        console.log(resolve, reject, 'object then')
        resolve(200)
      }
    }
  }).then(val => {
    console.log(val, ', p then 2 resolve')
  })
100 , p then 1 resolve
[Function (anonymous)] [Function (anonymous)] object then
200 , p then 2 resolve

如果返回的对象类型属性有一个 then 函数的话,则 MyPromise 的处理是将 resolve 和 reject 封装一层传递给外部的 then ,由它决定何时使用?。

注释 resolve(200) 之后:

100 , p then 1 resolve
[Function (anonymous)] [Function (anonymous)] object then

对比下原生的 Promise 呢,将 MyPromise 换成 Promise

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p = new Promise(resolve => {
    Util.delay(resolve(100))
  }).then(val => {
    console.log(val, ', p then 1 resolve')

    return {
      name: 1,
      then(resolve, reject) {
        console.log(resolve, reject, 'object then')
        resolve(200)
      }
    }
  }).then(val => {
    console.log(val, ', p then 2 resolve')
  })
100 , p then 1 resolve
[Function (anonymous)] [Function (anonymous)] object then
200 , p then 2 resolve

结果和 MyPromise 一样,如果在 object then 中不调用 resolve 或 reject 结果, 那么 then 链也会断开:

100 , p then 1 resolve
[Function (anonymous)] [Function (anonymous)] object then

疑问 1: foreign then 的 resolve 为什么要判断返回值是不是等于该对象自身?

1
2
3
4
5
  if (value !== thenable) {
    resolve(promise, value);
  } else {
    fulfill(promise, value);
  }

:如果 value === thenable ,直接调用 resolve(promise, value) 会造成死循 环 resolve -> value.then is function -> handleMaybeThenable -> handleForeignThenable -> resolve -> …

验证方法,将 asap 延迟时间加大,并且给 asap 套一层,加上延时时间,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
  function asap(callback) {
    queue[qlen] = callback;

    qlen++;

    if (qlen === 1) {
      // 这里是通过第一次入列操作来触发 flush 队列操作
      // 因为 flush 是异步执行,所以在它还没之前之前有可能有新的任务入列
      // 这个时候 qlen > 1 ,直到 flush 执行,通过 qlen 遍 queue 确保在执行的时刻
      // 可以将这之前的所有入列的任务都得到执行
      setTimeout(flush, 3000);
    }
  }

  let bakAsap = asap
  asap = cb => setTimeout(bakAsap(cb), 500)

这样可以避免卡死,每隔 500ms 会执行一次 asap,3000ms 之后 flush queue。

修改如下,直接 resolve:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  (value) => {
    if (sealed) return;
    sealed = true;

    resolve(promise, value);

    // if (value !== thenable) {
    //   resolve(promise, value);
    // } else {
    //   fulfill(promise, value);
    // }
  }

修改用例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
  let i = 0
  new MyPromise(resolve => {
    Util.delay(resolve(100))
  }).then(val => {
    console.log(val, ', p then 1 resolve')

    const obj = {
      name: 1,
      then(resolve, reject) {
        console.log(i++, ', object then')
        resolve(obj)
      }
    }
    return obj
  }).then(val => {
    console.log(val, ', p then 2 resolve')
  })

执行结果:

 ➜  js git:(master) ✗ node promise.js
 100 , p then 1 resolve
 0 , object then
 1 , object then
 2 , object then
 3 , object then
 4 , object then
 5 , object then
 6 , object then
 // 如果不停止会一直执行下去,因为死循环了

Promise Api 实现

ecma262 文档中我们知道 Promise 有如下 APIs:

名称简介
Promiseall(iterable)满足条件:所有 promises 都 fulfilled
allSettled(iterable)不在乎结果是 FULFILL 还是 REJECT,只要所有的任务状态都改变了就 FULFILL 否则 REJECT
any(iterable)只要有一个任务 FULFILL 结果就是 FULFILL 否则 REJECT
race(iterable)竞争关系,第一个状态改变发生改变 race 状态就跟着改变,是啥就是啥,FULFILL -> FULFILL, REJECT -> REJECT
reject(rejectHandler)返回一个必定 REJECT 的 promise
resolve(fulfillHandler)返回一个必定 FULFILL 的 promise
Promise.prototypecatch(onRejected)
finally(onFinally)
then(onFulfilled, onRejected)

当目前表中的函数只实现了 thenresolve 下面将一一实现它们。

Promise.reject(rejectHandler)

1
2
3
4
5
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  Promise.reject('test reject result').then(null, reason => {
    console.log(reason, ', p reject then')
  })
test reject result , p reject then

这个跟 Promise.resolve 一样,直接创建一个 Promise 实例 ins,调用 reject(ins, reason)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12

  function originalReject(reason) {
    const ctor = this;

    if (typeof value === "object" && value.constructor === ctor) {
      return value;
    }

    const ins = new ctor(noop);
    reject(promise, reason);
    return ins;
  }

Promise.race(entries)

race 竞争机制,只要有一个 fulfilled 了就立即结束。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p1 = new MyPromise(resolve => {
    Util.delay(() => resolve(100), 100)
  })
  const p2 = new MyPromise((resolve, reject) => {
    Util.delay(() => reject(200), 80)
  })

  MyPromise.race([p1, p2]).then(val => {
    console.log(val, ', race then resolve')
  }, reason => {
    console.log(reason, ', race then reject')
  })
200 , race then reject

只要有一个状态改变了就里面结束 race ,它不在乎结束的时候那个 promise 是 fulfilled 还是 rejected

实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
  MyPromise.race = function (entries) {
    const ctor = this;

    if (!Array.isArray(entries)) {
      return new ctor((_, reject) =>
        reject(new TypeError("race 参数必须是一个数组"))
      );
    } else {
      return new ctor((resolve, reject) => {
        // 遍历所有任务
        const len = entries.length;
        for (let i = 0; i < len; i++) {
          // 直接调用 resolve 去执行任务,然后挂一个 then
          // 因为 resolve 和 reject 只会被执行一次,所以一旦只要有个 entry
          // 结束了就会执行后面的 then 去调用 resolve 或 reject,
          // 后面的就算执行到了也 settled 了,也不会重复执行 resolve 和 reject
          ctor.resolve(entries[i]).then(resolve, reject);
        }
      });
    }
  }

Promise.any(entries)

只要有个 promise fulfilled 了,返回的 promise 状态就会变成 fulfilled,否则是 rejected,并且 rejected then 的回调接受的参数 reason 会是 entries 中所有 rejected promise 结果值数组.

修改点:将应该 fulfill() 地方换成 reject()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  function Enumerator(entries, option) {
    var p = (this.promise = new Ctor(noop));
    this.option = option || {};

    // 保存结束的函数,所有任务都 settled 之后调用的函数
    // Promise.any 所有的 REJECT 会调用 reject
    // Promise.all 所有的都 FULFILL 会调用 fulfill
    // 所以要区分开
    this.$settle = opt.isPromiseAnyFlag ? reject : fulfill;
    if (Array.isArray(entries)) {

      // ... 省略
      if (this._length === 0) {
        // 结束了
        this.$settle(p, this._result);
      } else {
        this._enumerate(entries);
        if (this._remaining === 0) {
          // 所有任务状态改变了
          this.$settle(p, this._result);
        }
      }
    } else {
      // 必须是数组类型
      reject(p, new TypeError("必须提供数组类型"));
    }
  }

  Enumerator.prototype._enumerate = function (entries) {
    var p = this.promise;
    // 当前 promise 状态处于 PENDING 状态下进行遍历
    for (var i = 0; p._state === PENDING && i < entries.length; i++) {
      var entry = entries[i];
      var then, error;

      try {
        then = entry.then;
      } catch (e) {
        error = e;
      }

      if (then === originalThen && entry._state !== PENDING) {
        // ... 省略
      } else if (typeof then !== "function") {
        // ... 省略
      } else if (p.constructor === Ctor) {
        var promise = new Ctor(noop);
        if (error) {
          // 这里如果执行错误要区分下是 Promise.any 还是 Promise.all
          if (this.option.isPromiseAnyFlag) {
            this._result[i] = error;
            this._remaining--;
          } else {
            reject(entry, error);
          }
        } else {
        }

        // ...
      } else {
        // ...
      }
    }
  };

  Enumerator.prototype._settle = function (state, i, result) {
    var p = this.promise;
    var opt = this.option;

    if (p._state === PENDING) {
      this._remaining--;

      // 修改点
      // for Promise.any
      if (opt.isPromiseAnyFlag) {
        if (state === REJECT) {
          this._result[i] = result;
        } else {
          resolve(p, result);
        }
      } else {
        // ...
      }
    }

    if (this._remaining === 0) {
      // Promise.any 应该 reject
      // 修改点
      this.$settle(p, this._result);
    }
  };

测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p1 = new MyPromise(resolve => {
    Util.delay(() => resolve(100), 100)
  })
  const p2 = new MyPromise((resolve, reject) => {
    Util.delay(() => reject(200), 80)
  })

  const p3 = new MyPromise((resolve, reject) => {
    Util.delay(() => reject(300), 80)
  })

  MyPromise.any([p1, p2]).then(val => {
    console.log(val, ', any then resolve 1')
  }, reason => {
    console.log(reason, ', any then reject 1')
  })

  MyPromise.any([p2, p3]).then(val => {
    console.log(val, ', any then resolve 2')
  }, reason => {
    console.log(reason, ', any then reject 2')
  })
[ 200, 300 ] , any then reject 2
100 , any then resolve 1

第一行输出:p2, p3 都是 REJECT ,所以最后结果是 REJECT 第二行输出:p1, p2 中 p1 是 FULFILL ,所以最后结果是 FULFILL

Promise.all(entries)

all 函数的实现关键:要等到所有的 promise 状态都 settled 了,才能 fulfill。

然后任务有异步也可能有同步任务(即 promise 状态是否立即改变),不管如何都要等到他 们状态发生改变了之后才能让 all 结束,所以两种处理情况

  1. 状态立即发生改变了的,直接记录

  2. 状态是 PENDING 的注册记录回调直到所有任务都完成

  3. 如果遇到有一个 rejected 的立即结束,否则等待所有都 fulfilled 才结束,并且将所 有 fulfilled 结果组成数组传递给下一个 promise。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  // 简化版本,只处理:
  // 1. entry 是 promise 类型,
  // 2. entry 是普通类型(非 promise,then 不是函数)
  Enumerator.prototype._enumerateSimple = function (entries) {
    var p = this.promise;
    for (var i = 0; p._state === PENDING && i < entries.length; i++) {
      var entry = entries[i];

      if (entry.constructor === Ctor) {
        // 是个 promise 任务
        if (entry._state !== PENDING) {
          this._settle(entry._state, i, entry._result);
        } else {
          this._willSettle(entry, i);
        }
      } else {
        // 非 promise 类型处理
        this._remaining--;
        this._result[i] = entry;
      }
    }
  };

  Enumerator.prototype._enumerate = function (entries) {
    var p = this.promise;
    // 当前 promise 状态处于 PENDING 状态下进行遍历
    for (var i = 0; p._state === PENDING && i < entries.length; i++) {
      var entry = entries[i];
      var then, error;

      try {
        then = entry.then;
      } catch (e) {
        error = e;
      }

      if (then === originalThen && entry._state !== PENDING) {
        // entry 不一定是 Promise 但有 then 函数,且状态改变了的
        this._settle(entry._state, i, entry._result);
      } else if (typeof then !== "function") {
        // 普通类型值
        this._remaining--;
        this._result[i] = entry;
      } else if (p.constructor === Ctor) {
        // 到这里前提条件:
        // 1. then 不是 originalThen 或 entry._state = PENDING
        // 2. then 是个函数
        // 3. this.promise 是我们定义的 MyPromise
        // 那么将 entry 用 MyPromise 封装
        var promise = new Ctor(noop);
        if (error) {
          reject(entry, error);
        } else {
          handleMaybeThenable(promise, entry, then);
        }

        this._willSettle(promise, i);
      } else {
        this._willSettle(new Ctor((resolve) => resolve(entry)), i);
      }
    }
  };

  Enumerator.prototype._settle = function (state, i, result) {
    var p = this.promise;

    if (p._state !== PENDING) {
      this._remaining--;

      if (state === REJECT) {
        reject(p, result);
      } else {
        this._result[i] = result;
      }
    }

    if (this._remaining === 0) {
      fulfill(p, this._result);
    }
  };
  Enumerator.prototype._willSettle = function (promise, i) {
    // 订阅结果
    subscribe(
      promise,
      undefined,
      (value) => this._settle(FULFILL, i, value),
      (reason) => this._settle(REJECT, i, reason)
    );
  };

原作者的实现通过封装了一个 Enumerator 来实现 Promise.all 其中有一个构造函数 (Enumerator)和三个原型函数(_settle, _willSettle, _enumrate)。

  1. Enumerator 接受一个 entries 是一个数组

    构造函数初始化了几个变量:

    • _length 即 entries 任务的个数

    • _remaining 任务状态是 PENDING 的个数,通过检测这个是不是为零来判断是不是 所有任务都结束了。

    • _result 一个结果数组,保存所有任务执行的结果。

  2. _settle 检测当前 entry 的状态,如果是 REJECT 就让 this.promise REJECT ,结束循环,否则保存结果,最后检测 this._remaining

  3. _willSettle 因为 entry 的状态可能是 PENDING 所以要执行订阅,等待它结束再进 入 _settle 回到第 2 步。

所以这个函数的实现关键取决于 entry 状态是否是立即改变了,如果是直接检测结果,否 则构造该 entry 的 onFulfillmentonRejection 并执行订阅,等待它结束再检测 结果。最后根据结果决定 this.promise 状态,因为 Promise.all FULFILL 的条件 是所有任务都 FULFILL 才行,所以只要遇到一个 entry rejected 了那么 this.promise 就应该结束且状态是 REJECT

测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p1 = new MyPromise(resolve =>{
    Util.delay(() => resolve(100), 100)
  })

  const p2 = new MyPromise(resolve =>{
    Util.delay(() => resolve(200), 80)
  })

  const p3 = new MyPromise((_, reject) =>{
    Util.delay(() => reject(300), 80)
  })

  MyPromise.all([p1, p2]).then(result => {
    console.log(result, ', promise all 1')
  })

  MyPromise.all([p1, p3]).then(null, reason => {
    console.log(reason, ', promise all 2')
  })
300 , promise all 2
[ 100, 200 ] , promise all 1

改成原生 Promise 结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p1 = new Promise(resolve =>{
    Util.delay(() => resolve(100), 100)
  })

  const p2 = new Promise(resolve =>{
    Util.delay(() => resolve(200), 80)
  })

  const p3 = new Promise((_, reject) =>{
    Util.delay(() => reject(300), 80)
  })

  Promise.all([p1, p2]).then(result => {
    console.log(result, ', promise all 1')
  })

  Promise.all([p1, p3]).then(null, reason => {
    console.log(reason, ', promise all 2')
  })
300 , promise all 2
[ 100, 200 ] , promise all 1

结果 OK。

Promise.allSettled(entries)es2020

这个和 Promise.all 区别在于,它不在乎 entries 中任务的状态是 FULFILL 还是 REJECT , 只要它状态 settled 即可,且会等到所有任务都 settled 了才会 FULFILL

所以这里的实现和 Promise.all 会有所差别,但依然可以通过对 Enumerator 做细微 改动来实现。

修改点:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  function Enumerator(entries, option) {
    var p = (this.promise = new Ctor(noop));
    // 修改点1:增加 Promise.allSettled flag
    this.isAllSettledFlag = false;
    if (typeof option === "object") {
      this.isAllSettledFlag = !!option.isAllSettledFlag;
    }

    // ... 省略
  }


  Enumerator.prototype._settle = function (state, i, result) {
    var p = this.promise;

    if (p._state === PENDING) {
      this._remaining--;

      // api 不是 Promise.allSettled
      // 修改点2:增加判断是不是 Promise.allSettled 调用
      if (state === REJECT && !this.isAllSettledFlag) {
        reject(p, result);
      } else {
        this._result[i] = result;
      }
    }

    if (this._remaining === 0) {
      fulfill(p, this._result);
    }
  }

测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  const p1 = new MyPromise(resolve =>{
    Util.delay(() => resolve(100), 100)
  })

  const p2 = new MyPromise(resolve =>{
    Util.delay(() => resolve(200), 80)
  })

  const p3 = new MyPromise((_, reject) =>{
    Util.delay(() => reject(300), 80)
  })

  MyPromise.allSettled([p1, p2]).then(result => {
    console.log(result, ', promise allSettled 1 resolve')
  })

  MyPromise.allSettled([p1, p3]).then(value => {
    console.log(value, ', promise allSettled 2 resolve')
  })
[ 100, 200 ] , promise allSettled 1 resolve
[ 100, 300 ] , promise allSettled 2 resolve

Promise.prototype.finally(onFinally)

这个函数目的就是不管 promise 任务最重是 fulfilled 还是 rejected 都会被执行。

实现如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
  function originalFinally(callback) {
    var promise = this;
    var ctor = promise.constructor;
    // 1. 保证 callback 总是执行,即相当于在最后又挂了个 then,
    //   这样就能保证之前有多少 then 且这些 then 结果是 fulfilled 还是 rejected
    //   这个都会被执行
    // 2. 回调 callback 要被执行且要保证 callback 的执行结果
    //   也能符合 promise then 链规则
    if (typeof callback === "function") {
      return promise.then(
        (value) => ctor.resolve(callback(value)).then(() => value),
        (reason) =>
        ctor.resolve(callback(reason)).then(() => {
          throw reason;
        })
      );
    }

    return promise.then(callback, callback);
  }

测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  new Promise(resolve => {
    Util.delay(resolve(100))
  }).then(val => {
    console.log(val, 'then 1')
  }).finally(() => {
    console.log('finally 1')
  }).then((val) => {
    console.log(val, 'then 2')
    // 这里引用未声明类型,会报错
    return a + b
  }, reason => {
    console.log(reason.message, 'reject 2')
  }).finally(() => {
    console.log('finally 2')
  }).finally(100).then(val => {
    console.log(val, 'then 3')
  }, reason => {
    console.log(reason.message, 'reject 3')
  })

原生 Promise 执行结果

100 then 1
finally 1
undefined then 2
finally 2
a is not defined reject 3

注意这里有几点功能需要完成:

  1. finally 总是要被执行

  2. finally 的参数可以是普通类型的值

  3. finally 调用之后后面还可以继续链式调用,即它执行完也要返回一个 Promise 实例

换成 MyPromise:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  new MyPromise(resolve => {
    Util.delay(resolve(100))
  }).then(val => {
    console.log(val, 'then 1')
  }).finally(() => {
    console.log('finally 1')
  }).then((val) => {
    console.log(val, 'then 2')
    // 这里引用未声明类型,会报错
    return a + b
  }, reason => {
    console.log(reason.message, 'reject 2')
  }).finally(() => {
    console.log('finally 2')
  }).finally(100).then(val => {
    console.log(val, 'then 3')
  }, reason => {
    console.log(reason.message, 'reject 3')
  })
100 then 1
finally 1
undefined then 2
finally 2
a is not defined reject 3

使用 MyPromise 之后又两个异常,被后面两个 then rejection 捕获到了: promise is not defined ,这说明 finally

增加返回值传递给 finally 回调,测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  new MyPromise(resolve => {
    Util.delay(resolve(100))
  }).then(val => {
    console.log(val, 'then 1')
    return 200
  }).finally((val) => {
    console.log(val, ', finally 1')
  })
100 then 1
200 , finally 1
undefined

Promise.prototype.catch(onRejection)

ECMA262标准流程

26.6.5.1 Promise.prototype.catch ( onRejected )
When the catch method is called with argument onRejected, the following steps are taken:

  1. Let promise be the this value.
  2. Return ? Invoke(promise, "then", « undefined, onRejected »).

实现:

1
2
3
4
5
  MyPromise.prototype.catch = function (onRejection) {
    // 因为如果有异常,异常会随着链式调用链中一直往后流,知道被处理掉
    // 所以这里只要挂一个 then 去接受错误并处理就可以了
    return this.then(null, onRejection);
  }

测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
  const { MyPromise, Util} = require(`${process.env.PWD}/../../static/js/promise.js`)

  new MyPromise(resolve => {
    Util.delay(resolve(100))
  }).then(val => {
    console.log(val, ', then 1')
    return a + b
  }).catch(err => {
    console.log(err.message)
    return 200
  }).then(val => {
    console.log(val, ', then 2')
  })
100 , then 1
a is not defined
200 , then 2

then 2 能够输出是因为 then 1 的错误被 catch 处理掉了,所以 then 2 可以正常 resolve。

完整脑图

/img/es/es6-promise.svg

Jest 测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  const Promise = require("./promise.js").MyPromise;
  const delay = (fn, t) => setTimeout(fn, t);
  const log = (...args) => console.log.apply(console, args);

  describe("Promise", () => {
    test("Promise then resolve", () => {
      return new Promise((resolve) => delay(() => resolve(100))).then((value) =>
        expect(value).toBe(100)
      );
    });
    test("Promise then reject", () => {
      return new Promise((_, reject) =>
        delay(() => reject("test reject"))
      ).then(null, (reason) => expect(reason).toBe("test reject"));
    });

    test("Promise catch", () => {
      return new Promise((resolve) => delay(() => resolve(100)))
        .then(() => a /* a is not defined */)
        .catch((err) => expect(err.message).toBe("a is not defined"));
    });

    test("Promise finally", () => {
      return new Promise((resolve) => delay(() => resolve(100)))
        .then((value) => 200)
        .finally((result) => expect(result).toBe(200));
    });

    test("Promise finally with catch", () => {
      return new Promise((resolve) => delay(() => resolve(100)))
        .then((value) => a)
        .catch((err) => err.message)
        .finally((result) => expect(result).toBe("a is not defined"));
    });

    test("Promise all", () => {
      const p1 = new Promise((resolve) => delay(() => resolve(100), 100));
      const p2 = new Promise((_, reject) => delay(() => reject(200), 200));
      const p3 = new Promise((_, reject) => delay(() => reject(300), 80));
      const p4 = new Promise((resolve) => delay(() => resolve(400), 120));

      let pa1 = Promise.all([p1, p4]).then((value) => value);
      let pa2 = Promise.all([p1, p2]).then(null, (reason) => reason);
      let pa3 = Promise.all([p2, p3]).then(null, (reason) => reason);
      return Promise.allSettled([pa1, pa2, pa3]).then((value) =>
        expect(value).toStrictEqual([[100, 400], 200, 300])
      );
    });

    test("Promise allSettled", () => {
      const p1 = new Promise((resolve) => delay(() => resolve(100), 100));
      const p2 = new Promise((_, reject) =>
        delay(() => reject("p2 reject"), 200)
      );
      const p3 = new Promise((resolve) => delay(() => resolve(300), 120));

      return Promise.allSettled([p1, p2, p3]).then((value) =>
        expect(value).toStrictEqual([100, "p2 reject", 300])
      );
    });

    test("Promise race resolve", () => {
      const p1 = new Promise((resolve) => delay(() => resolve(100), 100));
      const p2 = new Promise((_, reject) =>
        delay(() => reject("p2 reject"), 200)
      );
      return Promise.race([p2, p1]).then((value) => expect(value).toBe(100));
    });

    test("Promise race reject", () => {
      const p1 = new Promise((resolve) => delay(() => resolve(100), 100));
      const p2 = new Promise((_, reject) => delay(() => reject("p2 reject"), 80));
      return Promise.race([p2, p1]).then(null, (reason) =>
        expect(reason).toBe("p2 reject")
      );
    });

    test("Promise any resolve", () => {
      const p1 = new Promise((resolve) => delay(() => resolve(100), 100));
      const p2 = new Promise((_, reject) => delay(() => reject("p2 reject"), 80));
      const p3 = new Promise((_, reject) => delay(() => reject("p3 reject"), 80));
      return Promise.any([p2, p1, p3]).then((value) => expect(value).toBe(100));
    });

    test("Promise any reject", () => {
      const p1 = new Promise((resolve) => delay(() => resolve(100), 100));
      const p2 = new Promise((_, reject) => delay(() => reject("p2 reject"), 80));
      const p3 = new Promise((_, reject) => delay(() => reject("p3 reject"), 80));
      return Promise.any([p2, p3]).then(null, (value) =>
        expect(value).toStrictEqual(["p2 reject", "p3 reject"])
      );
    });
  });

结果:

  ➜  js git:(master) ✗ jest
  jest
  PASS ./promise.spec.js (5.19s)
    Promise
      ✓ Promise then resolve (11ms)
      ✓ Promise then reject (3ms)
      ✓ Promise catch (5ms)
      ✓ Promise finally (4ms)
      ✓ Promise finally with catch (6ms)
      ✓ Promise all (205ms)
      ✓ Promise allSettled (204ms)
      ✓ Promise race resolve (101ms)
      ✓ Promise race reject (85ms)
      ✓ Promise any resolve (102ms)
      ✓ Promise any reject (85ms)

  Test Suites: 1 passed, 1 total
  Tests:       11 passed, 11 total
  Snapshots:   0 total
  Time:        9.801s
  Ran all test suites.

总结

Promise 实现关键点:

  1. resolver 立即执行, resolve/reject 封装之后暴露给 resolver 的使用者调用,比 如:请求成功调用 resolve(value), 请求失败调用 reject(reason)

  2. resolve 函数实现需要判断各种情况,主要包含:

    • 不能处理自身,会造成死循环,即 then 的 callback 里面不能返回当前 promise 实 例自身

    • 如果是对象或函数,要考虑到提供了 then(resolve, reject) 函数情况

    • 是一个新的 Promise 实例。

  3. 任务分立即完成和延时完成情况,立即完成的代表状态立即改变了直接在 then 函数中 fulfillreject 即可,如果是异步的,需要通过订阅(subscribe)来实现。

  4. then 函数中为了链式调用,需要新建一个 child promise 作为返回值。

  5. Promise.all, allSettled, any 这几个 api 的实现通过 Enumerator 封装。

  6. Promise.all 实现原理,遍历所有任务,同步的直接记录结果,异步的订阅结果,异 步完成之后检测最终的值,整个过程中只要中间有任一个 REJECT 了, Promise.all() 状态立即改变为 REJECT 结束,返回结果是: FULFILL-结果数组, REJECT-失败原因。

  7. Promise.allSettled 实现原理与 Promise.all 大相径庭,只要将 Promise.all 里面 遇到 REJECT 的处理改成记录结果就行,最后所有的完成之后就 FULFILL ,返回结果 是所有任务 FULFILL 结果或所有 REJECT 原因组成的数组。

  8. Promise.any 的实现与 Promise.all 刚好相反,它只要遇到有一个 FULFILL 就结束, 返回 FULFILL 状态,除非所有的都 REJECT 了才会 REJECT ,返回结果值为: FULFILL-成功结果值, REJECT-返回所有失败任务的原因组成的数组。