0. 前言

面试官:「你写个 Promise 吧。」

我:「对不起,打搅了,再见!」

当初前端越来越卷,不会手写 Promise 都不好意思面试了(手动狗头.jpg)。尽管没多少人会在业务中用本人实现的 Promise,然而,实现 Promise 的过程会让你对 Promise 更加理解,出了问题也能够更好地排查。

如果你还不相熟 Promise,倡议先看一下 MDN 文档。

在实现 Promise 之前,我倡议你先看一遍 Promises/A+ 标准(中文翻译:Promise A+ 标准),本文中不会再次介绍相干的概念。举荐大家优先浏览原版英文,只需高中程度的英语常识就够了,遇到不懂的再看译文。

另外,本文将应用 ES6 中的 Class 来实现 Promise。为了不便大家跟 Promise/A+ 标准对照着看,下文的程序将依照标准的程序来行文。

在正式开始之前,咱们新建一个我的项目,名称随便,依照以下步骤进行初始:

  • 关上 CMD 或 VS Code,运行 npm init,初始化我的项目
  • 新建 PromiseImpl.js 文件,前面所有的代码实现都写在这个文件里

残缺代码地址:ashengtan/promise-aplus-implementing

1. 术语

这部分大家间接看标准就好,也没什么好解释的。留神其中对于 value 的形容,value 能够是一个 thenable(有 then 办法的对象或函数) 或者 Promise,这点会在前面的实现中体现进去。

2. 要求

2.1 Promise 的状态

一个 Promise 有三种状态:

  • pending:初始状态
  • fulfilled:胜利执行
  • rejected:拒绝执行

一个 Promise 一旦从 pending 变为 fulfilledrejected,就无奈变成其余状态。当 fulfilled 时,须要给出一个不可变的值;同样,当 rejected 时,须要给出一个不可变的起因。

依据以上信息,咱们定义 3 个常量,用来示意 Promise 的状态:

const STATUS_PENDING = 'pending'const STATUS_FULFILLED = 'fulfilled'const STATUS_REJECTED = 'rejected'

接着,咱们先把 Promise 的根底框架先定义进去,这里我应用 ES6 的 Class 来定义:

class PromiseImpl {  constructor() {}  then(onFulfilled, onRejected) {}}

这里咱们先回忆一下 Promise 的根本用法:

const promise = new Promise((resolve, reject) => {  // ...do something  resolve(value) // or reject(error)})// 屡次调用const p1 = promise.then()const p2 = promise.then()const p3 = promise.then()

好了,持续欠缺 PromiseImpl,先欠缺一下构造方法:

class PromiseImpl {  constructor() {    // `Promise` 以后的状态,初始化时为 `pending`    this.status = STATUS_PENDING    // fulfilled 时的值    this.value = null    // rejected 时的起因    this.reason = null  }}

另外,咱们还要定义两个办法,用于 fulfilledrejected 时回调:

class PromiseImpl {  constructor() {    // ...其余代码    // 2.1.2 When `fulfilled`, a `promise`:    //  2.1.2.1 must not transition to any other state.    //  2.1.2.2 must have a value, which must not change.    const _resolve = value => {      // 如果 `value` 是 `Promise`(即嵌套 `Promise`),      // 则须要期待该 `Promise` 执行实现      if (value instanceof PromiseImpl) {        return value.then(          value => _resolve(value),          reason => _reject(reason)        )      }            if (this.status === STATUS_PENDING) {        this.status = STATUS_FULFILLED        this.value = value      }    }    // 2.1.3 When `rejected`, a `promise`:    //  2.1.3.1 must not transition to any other state.    //  2.1.3.2 must have a reason, which must not change.    const _reject = reason => {      if (this.status === STATUS_PENDING) {        this.status = STATUS_REJECTED        this.reason = reason      }    }  }}

留神,在 _resolve() 中,如果 valuePromise 的话(即嵌套 Promise),则须要期待该 Promise 执行实现。这点很重要,因为前面的其余 API 如 Promise.resolvePromise.allPromise.allSettled 等均须要期待嵌套 Promise 执行实现才会返回后果。

最初,别忘了在 new Promise() 时,咱们须要将 resolvereject 传给调用者:

class PromiseImpl {  constructor(executor) {    // ...其余代码    try {      executor(_resolve, _reject)    } catch (e) {      _reject(e)    }  }}

应用 trycatchexecutor 包裹起来,因为这部分是调用者的代码,咱们无奈保障调用者的代码不会出错。

2.2 Then 办法

一个 Promise 必须提供一个 then 办法,其承受两个参数:

promise.then(onFulfilled, onRejected)
class PromiseImpl {  then(onFulfilled, onRejected) {}}

2.2.1 onFulfilledonRejected

从标准 2.2.1 中咱们能够得悉以下信息:

  • onFulfilledonRejected 是可选参数
  • 如果 onFulfilledonRejected 不是函数,则必须被疏忽

因而,咱们能够这样实现:

class PromiseImpl {  then(onFulfilled, onRejected) {    // 2.2.1 Both `onFulfilled` and `onRejected` are optional arguments:    //   2.2.1.1 If `onFulfilled` is not a function, it must be ignored    //   2.2.1.2 If `onRejected` is not a function, it must be ignored    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {}    onRejected = typeof onRejected === 'function' ? onRejected : () => {}  }}

2.2.2 onFulfilled 个性

从标准 2.2.2 中咱们能够得悉以下信息:

  • 如果 onFulfilled 是一个函数,则必须在 fulfilled 后调用,第一个参数为 promise 的值
  • 只能调用一次
class PromiseImpl {  then(onFulfilled, onRejected) {    // ...其余代码        // 2.2.2 If `onFulfilled` is a function:    //   2.2.2.1 it must be called after `promise` is fulfilled,    // with promise’s value as its first argument.    //   2.2.2.2 it must not be called before `promise` is fulfilled.    //   2.2.2.3 it must not be called more than once.    if (this.status === STATUS_FULFILLED) {      onFulfilled(this.value)    }  }}

2.2.3 onRejected 个性

onFulfilled 同理,只不过是在 rejected 时调用,第一个参数为 promise 失败的起因

class PromiseImpl {  then(onFulfilled, onRejected) {    // ...其余代码    // 2.2.3 If onRejected is a function:    //   2.2.3.1 it must be called after promise is rejected,    // with promise’s reason as its first argument.    //   2.2.3.2 it must not be called before promise is rejected.    //   2.2.3.3 it must not be called more than once.    if (this.status === STATUS_REJECTED) {      onRejected(this.reason)    }  }}

2.2.4 异步执行

在日常开发中,咱们常常应用 Promise 来做一些异步操作,标准 2.2.4 就是规定异步执行的问题,具体的能够联合标准里的正文浏览,重点是确保 onFulfilledonRejected 要异步执行。

须要指出的是,标准里并没有规定 Promise 肯定要用 micro-task 机制来实现,因而你应用 macro-task 机制来实现也是能够的。当然,当初浏览器用的是 micro-task 来实现。这里为了不便,咱们应用 setTimeout(属于 macro-task)来实现。

因而,咱们须要略微革新下下面的代码:

class PromiseImpl {  then(onFulfilled, onRejected) {    // ...其余代码        // fulfilled    if (this.status === STATUS_FULFILLED) {      setTimeout(() => {        onFulfilled(this.value)      }, 0)    }    // rejected    if (this.status === STATUS_REJECTED) {      setTimeout(() => {        onRejected(this.reason)      }, 0)    }  }}

2.2.5 onFulfilledonRejected 必须作为函数被调用

这个曾经在下面实现过了。

2.2.6 then 可被屡次调用

举个例子:

const promise = new Promise((resolve, reject) => {  // ...do something  resolve(value) // or reject(error)})promise.then()promise.then()promise.catch()

因而,必须确保当 Promise fulfilledrejected 时,onFulfilledonRejected 依照其注册的程序逐个回调。还记得最开始咱们定义的 resolvereject 吗?这里咱们须要革新下,保障所有的回调都被执行到:

const invokeArrayFns = (fns, arg) => {  for (let i = 0; i < fns.length; i++) {    fns[i](arg)  }}class PromiseImpl {  constructor(executor) {    // ...其余代码    // 用于寄存 `fulfilled` 时的回调,一个 `Promise` 对象能够注册多个 `fulfilled` 回调函数    this.onFulfilledCbs = []    // 用于寄存 `rejected` 时的回调,一个 `Promise` 对象能够注册多个 `rejected` 回调函数    this.onRejectedCbs = []    const resolve = value => {      if (this.status === STATUS_PENDING) {        this.status = STATUS_FULFILLED        this.value = value        // 2.2.6.1 If/when `promise` is fulfilled,         // all respective `onFulfilled` callbacks must execute         // in the order of their originating calls to `then`.        invokeArrayFns(this.onFulfilledCbs, value)      }    }    const reject = reason => {      if (this.status === STATUS_PENDING) {        this.status = STATUS_REJECTED        this.reason = reason        // 2.2.6.2 If/when `promise` is rejected,         // all respective `onRejected` callbacks must execute         // in the order of their originating calls to `then`.        invokeArrayFns(this.onRejectedCbs, reason)      }    }  }}

看到这里你可能会有疑难,什么时候往 onFulfilledCbsonRejectedCbs 里寄存对应的回调,答案是在调用 then 时:

class PromiseImpl {  then(onFulfilled, onRejected) {    // ...其余代码    // pending    if (this.status === STATUS_PENDING) {      this.onFulfilledCbs.push(() => {        setTimeout(() => {          onFulfilled(this.value)        }, 0)      })      this.onRejectedCbs.push(() => {        setTimeout(() => {          onRejected(this.reason)        }, 0)      })    }  }}

此时 Promise 处于 pending 状态,无奈确定其最初是 fulfilled 还是 rejected,因而须要将回调函数寄存起来,待状态确定后再执行相应的回调函数。

注:invokeArrayFns 来源于 Vue.js 3 中的源码。

2.2.7 then 必须返回 Promise

promise2 = promise1.then(onFulfilled, onRejected)

那么,在咱们下面的代码中,then 怎么能力返回 Promise 呢?很简略:

class PromiseImpl {  then(onFulfilled, onRejected) {    let promise2 = new PromiseImpl((resolve, reject) => {      if (this.status === STATUS_FULFILLED) {        // ...相干代码      }      if (this.status === STATUS_REJECTED) {        // ...相干代码      }      if (this.status === STATUS_PENDING) {        // ...相干代码      }    })    return promise2  }}

因为调用 then 之后返回一个新的 Promise 对象,使得咱们也能够进行链式调用:

Promise.resolve(42).then().then()...

2.2.7.1 ~ 2.2.7.4 这四点比拟重要,咱们上面别离来看看。

2.2.7.1 如果 onFulfilledonRejected 返回一个值 x,则运行 Promise 解决过程,[[Resolve]](promise2, x)

解释:其实所谓运行 Promise 解决过程就是执行某个操作,咱们把这个操作抽取成一个办法,并命名为:promiseResolutionProcedure(promise, x, resolve, reject)。为了不便,咱们把 resolvereject 一并透传进去。

class PromiseImpl {  then(onFulfilled, onRejected) {    // ...其余代码        let promise2 = new PromiseImpl((resolve, reject) => {      if (this.status === STATUS_FULFILLED) {        setTimeout(() => {          // 2.2.7.1          let x = onFulfilled(this.value)          promiseResolutionProcedure(promise2, x, resolve, reject)        }, 0)      }      if (this.status === STATUS_REJECTED) {        setTimeout(() => {          // 2.2.7.1          let x = onRejected(this.reason)          promiseResolutionProcedure(promise2, x, resolve, reject)        }, 0)      }      if (this.status === STATUS_PENDING) {        this.onFulfilledCbs.push(() => {          setTimeout(() => {            // 2.2.7.1             let x = onFulfilled(this.value)            promiseResolutionProcedure(promise2, x, resolve, reject)          }, 0)        })        this.onRejectedCbs.push(() => {          setTimeout(() => {            // 2.2.7.1            let x = onRejected(this.reason)            promiseResolutionProcedure(promise2, x, resolve, reject)          }, 0)        })      }    })    return promise2  }}

2.2.7.2 如果 onFulfilledonRejected 抛出一个异样 e,则 promise2 必须 rejected,并返回起因 e

解释:实现下面体现在执行 onFulfilledonRejected 时应用 trycatch 包含起来,并在 catch 时调用 reject(e)

class PromiseImpl {  then(onFulfilled, onRejected) {    // ...其余代码        let promise2 = new PromiseImpl((resolve, reject) => {      if (this.status === STATUS_FULFILLED) {        setTimeout(() => {          try {            // 2.2.7.1            let x = onFulfilled(this.value)            promiseResolutionProcedure(promise2, x, resolve, reject)          } catch (e) {            // 2.2.7.2            reject(e)          }        }, 0)      }      if (this.status === STATUS_REJECTED) {        setTimeout(() => {          try {            // 2.2.7.1            let x = onRejected(this.reason)            promiseResolutionProcedure(promise2, x, resolve, reject)          } catch (e) {            // 2.2.7.2            reject(e)          }        }, 0)      }      if (this.status === STATUS_PENDING) {        this.onFulfilledCbs.push(() => {          setTimeout(() => {            try {              // 2.2.7.1              let x = onFulfilled(this.value)              promiseResolutionProcedure(promise2, x, resolve, reject)            } catch (e) {              // 2.2.7.2              reject(e)            }          }, 0)        })        this.onRejectedCbs.push(() => {          setTimeout(() => {            try {              // 2.2.7.1              let x = onRejected(this.reason)              promiseResolutionProcedure(promise2, x, resolve, reject)            } catch (e) {              // 2.2.7.2              reject(e)            }          }, 0)        })      }    })    // 2.2.7 `then` must return a promise    return promise2  }}

2.2.7.3 如果 onFulfilled 不是函数且 promise1 曾经 fulfilled,则 promise2 必须 fulfilled 且返回与 promise1 雷同的值。

解释:值的透传,例如:

Promise.resolve(42).then().then(value => console.log(value)) // 42

在第一个 then 时,咱们疏忽了 onFulfilled,那么在链式调用的时候,须要把值透传给前面的 then

class PromiseImpl {  then(onFulfilled, onRejected) {    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value    // ...其余代码  }}

2.2.7.4 如果 onRejected 不是函数且 promise1 曾经 rejected,则 promise2 必须 rejected 且返回与 promise1 雷同的起因。

解释:同理,起因也要透传:

Promise.reject('reason').catch().catch(reason => console.log(reason)) // 'reason'
class PromiseImpl {  then(onFulfilled, onRejected) {    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }    // ...其余代码  }}

2.3 Promise 解决过程

Promise 解决过程是一个形象的操作,输出一个 promise 和一个值,这里咱们将其命名为 promiseResolutionProcedure(promise, x, resolve, reject),并在调用时传入 resolvereject 两个办法, 别离用于在 fulfilledrejected 时调用。

2.3.1 如果 promisex 为同一个对象

如果 promisex 为同一个对象,回绝该 promise,起因为 TypeError

const promiseResolutionProcedure = (promise, x, resolve, reject) => {  // 2.3.1 If `promise` and `x` refer to the same object,  // reject `promise` with a `TypeError` as the reason  if (promise === x) {    return reject(new TypeError('`promise` and `x` refer to the same object, see: https://promisesaplus.com/#point-48'))  }  // ...其余代码}

2.3.2 如果 x 是一个 Promise 对象

如果 x 是一个 Promise 对象,则须要递归执行:

const promiseResolutionProcedure = (promise, x, resolve, reject) => {  // ...其余代码  // 2.3.2 If `x` is a promise, adopt its state:  //   2.3.2.1 If `x` is pending, `promise` must remain pending until `x` is fulfilled or rejected.  //   2.3.2.2 If/when `x` is fulfilled, fulfill `promise` with the same value.  //   2.3.2.3 If/when `x` is rejected, reject `promise` with the same reason.  if (x instanceof PromiseImpl) {    return x.then(      value => promiseResolutionProcedure(promise, value, resolve, reject),      reason => reject(reason)    )  }}

2.3.3 如果 x 是一个对象或函数

如果 x 是一个对象或函数:

2.3.3.1x.then 赋值给 x

解释:这样做有两个目标:

  • 防止对 x.then 的屡次拜访(这也是日常开发中的一个小技巧,当要屡次拜访一个对象的同一属性时,通常咱们会应用一个变量将该属性存储起来,防止屡次进行原型链查找)
  • 执行过程中 x.then 的值可能被扭转
const promiseResolutionProcedure = (promise, x, resolve, reject) => {  // ...其余代码  // 2.3.3 Otherwise, if x is an object or function  if ((x !== null && typeof x === 'object') || typeof x === 'function') {        // 2.3.3.1 Let `then` be `x.then`    let then = x.then  }}

2.3.3.2 如果在对 x.then 取值时抛出异样 e,则回绝该 promise,起因为 e

const promiseResolutionProcedure = (promise, x, resolve, reject) => {  // ...其余代码  if ((x !== null && typeof x === 'object') || typeof x === 'function') {        try {      // 2.3.3.1 Let `then` be `x.then`    } catch (e) {      // 2.3.3.2 If retrieving the property `x.then` results in a thrown exception `e`,      // reject `promise` with `e` as the reason.      reject(e)    }  }}

2.3.3.3 如果 then 是函数

如果 then 是函数,则将 x 作为 then 的作用域,并调用 then,同时传入两个回调函数,第一个名为 resolvePromise,第二个名为 rejectPromise

解释:意思就是在调用 then 的时候要指定其 this 值为 x,同时须要传入两个回调函数。这时候用 call() 来实现是最好不过了:

then.call(x, resolvePromise, rejectPromise)

2.3.3.3.1 ~ 2.3.3.3.4 总结起来的意思如下:

  • 2.3.3.3.1 如果 resolvePromise 被调用,则递归调用 promiseResolutionProcedure,值为 y。因为 Promise 中能够嵌套 Promise

    then.call(  x,  y => promiseResolutionProcedure(promise, y, resolve, reject),  rejectPromise)
  • 2.3.3.3.2 如果 rejectPromise 被调用,参数为 r,则拒绝执行 Promise,起因为 r

    then.call(  x,  y => promiseResolutionProcedure(promise, y, resolve, reject), // resolvePromise  r => reject(r) // rejectPromise)
  • 2.3.3.3.3 如果 resolvePromiserejectPromise 均被调用,或者被同一参数调用了屡次,则只执行首次调用

    解释:这里咱们能够通过设置一个标记位来解决,而后别离在 resolvePromiserejectPromise 这两个回调函数内做判断:

    // 初始化时设置为 falselet called = falseif (called) {    return}// `resolvePromise` 和 `rejectPromise` 被调用时设置为 truecalled = true
  • 2.3.3.3.4 调用 then 抛出异样 e 时,如果这时 resolvePromiserejectPromise 曾经被调用,则疏忽;否则回绝该 Promise,起因为 e
const promiseResolutionProcedure = (promise, x, resolve, reject) => {  // ...其余代码    let called = false  if ((x !== null && typeof x === 'object') || typeof x === 'function') {    try {      let then = x.then      if (typeof then === 'function') {        // 2.3.3.3 If `then` is a function, call it with `x` as `this`,        // first argument `resolvePromise`, and second argument `rejectPromise`        then.call(          // call it with `x` as `this`          x,          // `resolvePromise`          // 2.3.3.3.1 If/when `resolvePromise` is called with a value `y`,          // run `[[Resolve]](promise, y)`.          y => {            // 2.3.3.3.3 If both `resolvePromise` and `rejectPromise` are called,            // or multiple calls to the same argument are made,            // the first call takes precedence, and any further calls are ignored.            if (called) {              return            }            called = true            promiseResolutionProcedure(promise, y, resolve, reject)          },          // `rejectPromise`          // 2.3.3.3.2 If/when `rejectPromise` is called with a reason `r`,          // reject `promise` with `r`          r => {            // 2.3.3.3.3            if (called) {              return            }            called = true            reject(r)          }        )      } else {        // 2.3.3.4 If `then` is not a function, fulfill `promise` with `x`        resolve(x)      }    } catch (e) {      // 2.3.3.3.3      if (called) {        return      }      called = true      // 2.3.3.2 If retrieving the property `x.then` results in a thrown exception `e`,      // reject `promise` with `e` as the reason.      // 2.3.3.3.4 If calling `then` throws an exception `e`      //   2.3.3.3.4.1 If `resolvePromise` or `rejectPromise` have been called, ignore it      //   2.3.3.3.4.2 Otherwise, reject `promise` with `e` as the reason      reject(e)    }  }}

2.3.3.4 如果 then 不是函数,则执行该 promise,参数为 x

const promiseResolutionProcedure = (promise, x, resolve, reject) => {  // ...其余代码  if ((x !== null && typeof x === 'object') || typeof x === 'function') {    try {      let then = x.then      if (typeof then === 'function') {        // 2.3.3.3      } else {        // 2.3.3.4 If `then` is not a function, fulfill `promise` with `x`        resolve(x)      }    } catch (e) {    }  }}

2.3.4 如果 x 既不是对象也不是函数

如果 x 既不是对象也不是函数,执行该 promise,参数为 x

const promiseResolutionProcedure = (promise, x, resolve, reject) => {  // ...其余代码  if ((x !== null && typeof x === 'object') || typeof x === 'function') {     // 2.3.3  } else {    // 2.3.4 If `x` is not an object or function, fulfill `promise` with `x`    resolve(x)  }}

至此,咱们的自定义 Promise 曾经实现。这是源码:promise-aplus-implementing。

4. 如何测试

Promise/A+ 标准提供了一个测试脚本:promises-tests,你能够用它来测试你的实现是否符合规范。

PromiseImpl.js 里增加以下代码:

// PromiseImpl.jsconst STATUS_PENDING = 'pending'const STATUS_FULFILLED = 'fulfilled'const STATUS_REJECTED = 'rejected'const invokeArrayFns = (fns, arg) => {  // ...相干代码}const promiseResolutionProcedure = (promise, x, resolve, reject) => {  // ...相干代码}class PromiseImpl {  // ...相干代码}PromiseImpl.defer = PromiseImpl.deferred = () => {  const dfd = {}  dfd.promise = new PromiseImpl((resolve, reject) => {    dfd.resolve = resolve    dfd.reject = reject  })  return dfd}module.exports = PromiseImpl

而后在 package.json 里增加 scripts

// package.json{  "scripts": {    "test": "promises-aplus-tests PromiseImpl.js"  }}

而后,运行 npm run test,执行测试脚本,如果你的实现合乎 Promise/A+ 标准的话,所有测试用例均会通过。

5. 其余 API 的实现

Promise/A+ 标准只规定了 then() 办法的实现,其余的如 catch()finally() 等办法并不在标准里。但就实现而言,这些办法能够通过对 then() 办法或其余形式进行二次封装来实现。

另外,像是 all()race() 等这些办法,其参数为一个可迭代对象,如 ArraySetMap 等。那么,什么是可迭代对象依据 ES6 中的标准,要成为可迭代对象,一个对象必须实现 @@iterator 办法,即该对象必须有一个名为 @@iterator 的属性,通常咱们应用常量 Symbol.iterator 来拜访该属性。

依据以上信息,判断一个参数是否为可迭代对象,其实现如下:

const isIterable = value => !!value && typeof value[Symbol.iterator] === 'function'

Promise.resolve

Promise.resolve(value)静态方法,其参数有以下几种可能:

  • 参数是 Promise 对象
  • 参数是 thenable 对象(领有 then() 办法的对象)
  • 参数是原始值或不具备 then() 办法的对象
  • 参数为空

因而,其返回值由其参数决定:有可能是一个具体的值,也有可能是一个 Promise 对象:

class PromiseImpl {   static resolve(value) {    return new PromiseImpl((resolve, reject) => resolve(value))  }}

Promise.reject

Promise.reject(reason)静态方法,参数为 Promise 拒绝执行时的起因,同时返回一个 Promise 对象,状态为 rejected

class PromiseImpl {   static reject(reason) {    return new PromiseImpl((resolve, reject) => reject(reason))  }}

Promise.prototype.catch

Promise.prototype.catch(onRejected) 其实就是 then(null, onRejected) 的语法糖:

class PromiseImpl {  catch(onRejected) {    return this.then(null, onRejected)  }}

Promise.prototype.finally

顾名思义,不论 Promise 最初的后果是 fulfilled 还是 rejectedfinally 里的语句都会执行:

class PromiseImpl {  finally(onFinally) {    return this.then(      value => PromiseImpl.resolve(onFinally()).then(() => value),       reason => PromiseImpl.resolve(onFinally()).then(() => { throw reason })    )  }}

Promise.all

Promise.all(iterable)静态方法,参数为可迭代对象:

  • 只有当 iterable 里所有的 Promise 都胜利执行后才会 fulfilled,回调函数的返回值为所有 Promise 的返回值组成的数组,程序与 iterable 的程序保持一致。
  • 一旦有一个 Promise 拒绝执行,则状态为 rejected,并且将第一个拒绝执行的 Promise 的起因作为回调函数的返回值。
  • 该办法会返回一个 Promise
class PromiseImpl {  static all(iterable) {    if (!isIterable(iterable)) {      return new TypeError(`TypeError: ${typeof iterable} is not iterable (cannot read property Symbol(Symbol.iterator))`)    }    return new PromiseImpl((resolve, reject) => {      // `fulfilled` 的 Promise 数量      let fulfilledCount = 0      // 收集 Promise `fulfilled` 时的值      const res = []        for (let i = 0; i < iterable.length; i++) {        const iterator = iterable[i]        iterator.then(          value => {            res[i] = value            fulfilledCount++            if (fulfilledCount === iterable.length) {              resolve(res)            }          },          reason => reject(reason)        )      }    })  }}

测试一下:

const promise1 = Promise.resolve(42)const promise2 = new PromiseImpl((resolve, reject) => setTimeout(() => resolve('value2'), 1000))PromiseImpl.all([  promise1,  promise2]).then(values => console.log('values:', values))

后果:

values: [42, 'value2']

如同挺完满的,然而事实果真如此吗?认真看看咱们的代码,如果 iterable 是一个空的数组呢?如果 iterable 里有不是 Promise 的呢?就像这样:

PromiseImpl.all([])PromiseImpl.all([promise1, promise2, 'value3'])

这种状况下执行后面的代码,是得不到任何后果的。因而,代码还要再改良一下:

class PromiseImpl {  static all(iterable) {    if (!isIterable(iterable)) {      return new TypeError(`TypeError: ${typeof iterable} is not iterable (cannot read property Symbol(Symbol.iterator))`)    }    return new PromiseImpl((resolve, reject) => {      // `fulfilled` 的 Promise 数量      let fulfilledCount = 0      // 收集 Promise `fulfilled` 时的值      const res = []            // - 填充 `res` 的值      // - 减少 `fulfilledCount`      // - 判断所有 `Promise` 是否曾经全副胜利执行      const processRes = (index, value) => {        res[index] = value        fulfilledCount++        if (fulfilledCount === iterable.length) {          resolve(res)        }      }      if (iterable.length === 0) {        resolve(res)      } else {        for (let i = 0; i < iterable.length; i++) {          const iterator = iterable[i]                    if (iterator && typeof iterator.then === 'function') {            iterator.then(              value => processRes(i, value),              reason => reject(reason)            )          } else {            processRes(i, iterator)          }        }      }    })  }}

当初再来测试一下:

const promise1 = PromiseImpl.resolve(42)const promise2 = 3const promise3 = new PromiseImpl((resolve, reject) => setTimeout(() => resolve('value3'), 1000))PromiseImpl.all([  promise1,  promise2,  promise3,  'a']).then(values => console.log('values:', values))// 后果:values: [42, 3, 'value3', 'a']PromiseImpl.all([]).then(values => console.log('values:', values))// 后果:values: []

Promise.allSettled

Promise.allSettled(iterable)静态方法,参数为可迭代对象:

  • iterable 里所有的 Promise 都胜利执行或拒绝执行后才实现,返回值是一个对象数组。
  • 如果有嵌套 Promise,须要期待该 Promise 实现。
  • 返回一个新的 Promise 对象。

对于 allSettled,咱们能够在 all 的根底上进行封装:

class PromiseImpl {  static allSettled(iterable) {    if (!isIterable(iterable)) {      return new TypeError(`TypeError: ${typeof iterable} is not iterable (cannot read property Symbol(Symbol.iterator))`)    }    const promises = iterable.map(iterator => PromiseImpl.resolve(iterator).then(      value => ({ status: STATUS_FULFILLED, value }),      reason => ({ status: STATUS_REJECTED, reason })    ))    return PromiseImpl.all(promises)  }}

测试后果:

PromiseImpl.allSettled([  PromiseImpl.resolve(42),  PromiseImpl.reject('Oops!'),  PromiseImpl.resolve(PromiseImpl.resolve(4242)),  'a']).then(values => console.log(values))// 后果:// [//   { status: 'fulfilled', value: 42 },//   { status: 'rejected', reason: 'Oops!' },//   { status: 'fulfilled', value: 4242 },//   { status: 'fulfilled', value: 'a' }// ]

Promise.race

Promise.race(iterable)静态方法,参数为可迭代对象:

  • iterable 里的任一 Promise 胜利执行或拒绝执行时,应用该 Promise 胜利返回的值或拒绝执行的起因
  • 如果 iterable 为空,则处于 pending 状态
  • 返回一个新的 Promise 对象

例如:

Promise.race([  Promise.resolve(42),  Promise.reject('Oops!'),  'a']).then(values => console.log(values))  .catch(reason => console.log(reason))// 后果:42Promise.race([  Promise.reject('Oops!'),  Promise.resolve(42),  'a']).then(values => console.log(values))  .catch(reason => console.log(reason))// 后果:Oops!

实现如下:

class PromiseImpl {  static race(iterable) {    if (!isIterable(iterable)) {      return new TypeError(`TypeError: ${typeof iterable} is not iterable (cannot read property Symbol(Symbol.iterator))`)    }    return new PromiseImpl((resolve, reject) => {      if (iterable.length === 0) {        return      } else {        for (let i = 0; i < iterable.length; i++) {          const iterator = iterable[i]                    if (iterator && typeof iterator.then === 'function') {            iterator.then(              value => resolve(value),              reason => reject(reason)            )            return          } else {            resolve(iterator)            return          }        }      }    })  }}

这里要留神一点:别忘了应用 return 来完结 for 循环

测试后果:

PromiseImpl.race([  PromiseImpl.resolve(42),  PromiseImpl.reject('Oops!'),  'a']).then(values => console.log(values))  .catch(reason => console.log(reason))// 后果:42PromiseImpl.race([  PromiseImpl.reject('Oops!'),  PromiseImpl.resolve(42),  'a']).then(values => console.log(values))  .catch(reason => console.log(reason))// 后果:'Oops!'PromiseImpl.race([  'a',  PromiseImpl.reject('Oops!'),  PromiseImpl.resolve(42)]).then(values => console.log(values))  .catch(reason => console.log(reason))// 后果:'a'

6. 独特探讨

  1. 2.3.3.1 把 x.then 赋值给 then 中,什么状况下 x.then 的指向会被扭转?
  2. 2.3.3.3 如果 then 是函数 中,除了应用 call() 之外,还有什么其余形式实现吗?

7. 总结

实现 Promise,根本分为三个步骤:

  1. 定义 Promise 的状态
  2. 实现 then 办法
  3. 实现 Promise 解决过程

8. 写在最初

以前,我在意能不能自己实现一个 Promise,到处找文章,这块代码 Ctrl+C,那块代码 Ctrl+V。当初,我看重的是实现的过程,在这个过程中,你不仅会对 Promise 更加相熟,还能够学习如何将标准一步步转为理论代码。做对的事,远比把事件做对重要

如果你感觉这篇文章对你有帮忙,还请:点赞、珍藏、转发;如果你有疑难,请在评论区写进去,咱们一起探讨。同时也欢送关注我的公众号:前端笔记

参考资料

  • Promises/A+
  • Promise A+ 标准
  • 应用 Promise - MDN
  • Promise - MDN