> For the complete documentation index, see [llms.txt](https://anjia1.gitbook.io/web/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://anjia1.gitbook.io/web/js/runtime/promise/implement.md).

# 实现一个 Promise（2）

上一篇实现了 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()`

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

具体的代码实现，详见上一篇：[实现一个符合 Promises/A+ 规范的 Promise](/web/js/runtime/promise/implement-then-method.md)。

### 1.2 `Promise.prototype.catch()`

相当于 `then()` 的简写，代码如下：

```javascript
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()` 的用法：

```javascript
new Promise((resolve, reject) => {
    resolve(1)
    // reject(2)
}).finally((value) => {
    // 1. 始终输出 undefined，因为它的回调函数没有参数
    console.log('finally() value=', value)
    // 2. 若是正常结果，则此返回没人理，因为逻辑上是透传上一个的结果
    return 3
    // 3. 若是有异常了，则会把该异常结果传下去（此时就变成上一个的结果没人理了）
    // return Promise.reject('finally reject()')
    // throw Error('finally Error()')
}).then((value) => {
    console.log('value: ', value)
}, (reason) => {
    console.log('reason:', reason)
})
```

`finally()` 的代码示意，如下：

```javascript
MyPromise.prototype.finally = function (cb) {
    // 若构造函数未知，则可通过 this.constructor 获取
    //     在这个例子里，构造函数就是 MyPromise
    // 为了兼容 `cb()` 返回值的各种情况（同步or异步）
    //     统一将其处理成 Promise，等其执行完了再返回
    return this.then(
        (value) => MyPromise.resolve(cb()).then(() => value),
        (reason) => MyPromise.resolve(cb()).then(() => { throw reason })
    )
}
```

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

```javascript
new Promise((resolve, reject) => {
    resolve()
}).finally(() => {
    console.log('finally1')
}).then(() => {
    console.log('then')
}).finally(() => {
    console.log('finally2')
})
// 会输出：
// finally1
// then
// finally2
```

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

## 2. 静态方法

### 2.1 `Promise.resolve()`

要么返回一个<mark style="color:green;">新</mark>的 promise，要么返回<mark style="color:green;">参数</mark>本身。

代码示意，如下：

```javascript
MyPromise.resolve = function (x) {
    return (x instanceof MyPromise) ? x : (new MyPromise((resolve) => resolve(x)))
}
```

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

### 2.2 `Promise.reject()`

返回一个<mark style="color:green;">新</mark>的 promise 对象，用给定的 reason 来 reject。

```javascript
MyPromise.reject = function (r) {
    return new MyPromise((undefined, reject) => { reject(r) })
}
```

## 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（可迭代的），代码如下：

```javascript
const isIterable = function (x) {
    if (x !== undefined && x !== null && typeof x[Symbol.iterator] === 'function') {
        return true
    } else {
        throw new TypeError(`${typeof x} ${x} is not iterable (cannot read property Symbol(Symbol.iterator))`)
    }
}
```

内置的[可迭代对象](/web/js/data-type/object/iterable.md)有 `String`, `Array`, `TypedArray`, `Map`, `Set`, `Segments` 以及 `arguments`, `NodeList` 和 `function*`, `async function*`。

当参数不是 iterable（可迭代的）时，会报错。如下：

```log
rejected: TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
rejected: TypeError: object null is not iterable (cannot read property Symbol(Symbol.iterator))
rejected: TypeError: boolean true is not iterable (cannot read property Symbol(Symbol.iterator))
rejected: TypeError: boolean false is not iterable (cannot read property Symbol(Symbol.iterator))
rejected: TypeError: number 5 is not iterable (cannot read property Symbol(Symbol.iterator))
rejected: TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
```

#### （2）当参数是 empty 时

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

<table><thead><tr><th width="169.33333333333331">静态方法</th><th width="92">iterable</th><th>empty iterable 时返回的 promise</th></tr></thead><tbody><tr><td><code>race()</code></td><td>必须</td><td>永远 pending</td></tr><tr><td><code>all()</code></td><td>必须</td><td>fulfilled <code>[]</code></td></tr><tr><td><code>allSettled()</code></td><td>必须</td><td>fulfilled <code>[]</code></td></tr><tr><td><code>any()</code></td><td>必须</td><td>rejected <br><code>AggregateError: All promises were rejected</code></td></tr></tbody></table>

#### （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()`

返回一个<mark style="color:green;">新</mark>的 promise，用<mark style="color:green;">最先</mark> settled 的结果来 settle。

代码示意，如下：

```javascript
MyPromise.race = function (promises) {
    return new MyPromise((resolve, reject) => {
        // must be iterable promises
        if (isIterable(promises)) {
            for (let p of promises) {
                MyPromise.resolve(p).then(resolve, reject)
            }
        }
    })
}
```

### 3.2 `Promise.all()`

返回一个<mark style="color:green;">新</mark>的 promise，要么是用<mark style="color:green;">所有</mark> values 的数组来 fulfill，要么是用<mark style="color:green;">最先</mark>到的 reason 来 reject。

代码示意，如下：

```javascript
MyPromise.all = function (promises) {
    return new MyPromise((resolve, reject) => {
        // must be iterable promises
        if (isIterable(promises)) {
            const len = promises.length

            // empty iterable
            if (len === 0) {
                resolve([])
            }

            // iterable
            let result = new Array(len)
            let count = 0
            for (let i in promises) {
                MyPromise.resolve(promises[i])
                    .then((value) => {
                        count++
                        result[i] = value
                        if (count === len) {
                            resolve(result)
                        }
                    }).catch(reject)
            }
        }
    })
}
```

### 3.3 `Promise.allSettled()`

返回一个<mark style="color:green;">新</mark>的 promise，用一个 promise state snapshots 数组来 fulfill，且是在<mark style="color:green;">所有</mark>原始 promises 都 settled 了（可以 fulfilled 也可以 rejected）。

代码示意，如下：

```javascript
MyPromise.allSettled = function (promises) {
    return new MyPromise((resolve, reject) => {
        // must be iterable promises
        if (isIterable(promises)) {
            const len = promises.length

            // empty iterable
            if (len === 0) {
                resolve([])
            }

            // iterable
            let result = new Array(len)
            let count = 0
            for (let i in promises) {
                MyPromise.resolve(promises[i])
                    .then((value) => {
                        result[i] = {
                            status: 'fulfilled',
                            value
                        }
                    })
                    .catch((reason) => {
                        result[i] = {
                            status: 'rejected',
                            reason
                        }
                    })
                    .finally(() => {
                        count++
                        if (count === len) {
                            resolve(result)
                        }
                    })
            }
        }
    })
}
```

### 3.4 `Promise.any()`

返回一个<mark style="color:green;">新</mark>的 promise，要么是用<mark style="color:green;">最先</mark> fulfilled promise 来 fulfill，要么是用包含<mark style="color:green;">所有</mark> reasons 的聚合错误来 reject。

代码示意，如下：

```javascript
MyPromise.any = function (promises) {
    return new MyPromise((resolve, reject) => {
        // must be iterable promises
        if (isIterable(promises)) {
            const len = promises.length

            // empty iterable
            if (len === 0) {
                throw new AggregateError([], 'empty promises')
            }

            // iterable
            let errors = new Array(len)
            let errorCount = 0
            for (let i in promises) {
                MyPromise.resolve(promises[i])
                    .then((value) => resolve(value))
                    .catch((reason) => {
                        errorCount++
                        errors[i] = reason
                        if (errorCount === len) {
                            reject(new AggregateError(errors, 'All promises were rejected'))
                        }
                    })
            }
        }
    })
}
```
