实现一个 Promise(2)

除了 then 以外的所有方法

上一篇实现了 Promise 的核心方法 then(),本篇将在此基础上实现 Promise 的其它 instance method(实例方法)和 static method(静态方法)。

说明:

  • 在基于 then() 实现其它方法时,选择了用 then-catch-finally 的形式,主要是为了代码的可读性。理论上,底层库更应该用 then() 的双参数形式,毕竟会少返回一两层 promise。

  • 这里就不考虑不同的 Promise 实现之间的 interoperability(互操作)了,比如在 resolve() 里直接判断 x instanceof MyPromise,比如在其它方法里直接调用 MyPromise.resolve()。毕竟 Promise 已经成为语言的正式标准也有七八年了,再加上本文的主要目的是想“通过自己实现一个 Promise 来更好地理解其原理”。

1. 实例方法

then() 是 Promise 的 primitive(原始)方法,catch()finally() 都是基于它实现的。

1.1 Promise.prototype.then()

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    // ...
}

具体的代码实现,详见上一篇:实现一个符合 Promises/A+ 规范的 Promise

1.2 Promise.prototype.catch()

相当于 then() 的简写,代码如下:

MyPromise.prototype.catch = function (onRejected) {
    return this.then(undefined, onRejected)
}

1.3 Promise.prototype.finally()

是在 then() 的基础上实现的,也返回一个 promise(便于链式调用),不过逻辑上有以下不同:

  1. 只有一个回调参数,可以理解为形如 then(onFinally, onFinally)

  2. 调用时机:无论是 fulfilled 还是 rejected,都会被调用(因此通常用来处理二者的公共逻辑)

  3. 在调用链上,如何从前面接值:该回调函数没有参数,即不接收上一个 then 的执行结果

  4. 在调用链上,如何向后面传值:

    • 如果回调函数的执行正常,则单纯透传上一个 then 的执行结果(包括 fulfilled 和 rejected)

    • 如果回调函数的执行被 reject 了,则它会把自身的异常结果传下去,即触发下一个 then 的 reject 逻辑

可以运行以下代码,感受 finally() 的用法:

finally() 的代码示意,如下:

注意:finally() 的执行顺序取决于它在 then() 链式调用里的顺序,而并不是要“最后”执行。比如:

结合 finally 函数的功能语义,感觉它叫 settled 会更合适。但是考虑到它的使用场景,通常是处理一些不论是 fulfilled 还是 rejected 的通用逻辑,因此大多数情况都是放在链式调用的最后;即便是在中间位置,也是对上一个 then 的 finally 处理。站在这个角度理解,名字叫 finally 也说得通。

2. 静态方法

2.1 Promise.resolve()

要么返回一个的 promise,要么返回参数本身。

代码示意,如下:

通常,当我们不知道某个值是不是 promise 时,就可以用 Promise.resolve(x) 将其显式地转为 promise。

2.2 Promise.reject()

返回一个的 promise 对象,用给定的 reason 来 reject。

3. 静态方法之并发

async task concurrency,异步任务并发

JavaScript 本质上是 single-threaded(单线程的),因此在给定的瞬间只有一个 task 被执行,虽然看起来是并发的。JavaScript 中的 parallel execution(并行执行)只能通过 worker threads(工作线程)来实现。

Promise 类提供了 4 种 static method 来处理异步任务并发,包括:

  1. Promise.race() ES2015,ES6

  2. Promise.all() ES2015,ES6

  3. Promise.allSettled() ES2020

  4. Promise.any() ES2021

它们都接收一个 iterable promises(准确地说是 thenables),然后返回一个新的 promise。其中,iterable 可能包含一个或多个 non-promise value 或已经 settled 的 promise。

3.0 参数说明

(1)参数必须是 iterable

判断一个值是不是 iterable(可迭代的),代码如下:

内置的可迭代对象String, Array, TypedArray, Map, Set, Segments 以及 arguments, NodeListfunction*, async function*

当参数不是 iterable(可迭代的)时,会报错。如下:

(2)当参数是 empty 时

当参数是 empty iterable 时,不同方法的处理结果不同。如下:

静态方法
iterable
empty iterable 时返回的 promise

race()

必须

永远 pending

all()

必须

fulfilled []

allSettled()

必须

fulfilled []

any()

必须

rejected AggregateError: All promises were rejected

(3)当包含 non-promise value 时

参数里可能包含 non-promise value,所以统一用 Promise.resolve(p) 处理成 promise。

(4)当包含已经 settled 的 promise 时

对于参数里已经 settled 的 promise,依然按照异步的逻辑执行,即不做特殊处理。

不是 pending 状态的 promise,就称它已经 settled 了,既可以 fulfilled 也可以 rejected。

3.1 Promise.race()

返回一个的 promise,用最先 settled 的结果来 settle。

代码示意,如下:

3.2 Promise.all()

返回一个的 promise,要么是用所有 values 的数组来 fulfill,要么是用最先到的 reason 来 reject。

代码示意,如下:

3.3 Promise.allSettled()

返回一个的 promise,用一个 promise state snapshots 数组来 fulfill,且是在所有原始 promises 都 settled 了(可以 fulfilled 也可以 rejected)。

代码示意,如下:

3.4 Promise.any()

返回一个的 promise,要么是用最先 fulfilled promise 来 fulfill,要么是用包含所有 reasons 的聚合错误来 reject。

代码示意,如下:

Last updated