Promise

指标

  • Promise A+标准
  • 手写Promise
  • 题目练习

Promise A+标准

术语

  1. promise: 一个领有合乎这个标准的行为的then办法的对象或函数。
  2. thenable: 定义了一个then办法的对象或函数。
  3. value: 任意非法的JavaScript值(包含undefined,thenable,promise)。
  4. exception: 应用throw语句抛出的一个值
  5. reason: 示意promise为什么被回绝的一个值

必要条件

Promise 状态

promise必须是这三个状态中的一种:期待态pending,解决态fulfilled或回绝态rejected

  1. 当promise处于pending状态的时候:
    可能变为fulfilled或者rejected状态。
  2. 当promise处于fulfilled状态的时候:
    2.1 肯定不能转换为任何其它状态
    2.2 必须有一个不能扭转的value
  3. 当promise处于rejected状态的时候:
    3.1 肯定不能转换为任何其它状态
    3.2 必须有一个不能扭转的reason

在这里,"肯定不能扭转"意味着不变的身份(例如 ===),然而并不意味着深度不可变性。(译注者:这里应该是说只有值的援用雷同即可,并不需要援用中的每一个值都相等)

then办法

Promise必须提供一个then办法来拜访以后或最终的valuereason

Promise的then办法承受俩个参数:

promise.then(onFulfilled, onRejected)
  1. onFulfilledonRejected都是可选的参数
    1.1. 如果onFulfilled不是一个函数,它必须被疏忽
    1.2. 如果onRejected不是一个函数,它必须被疏忽
  2. 如果onFulfilled是一个函数
    2.1. 在promise变成 fulfilled 时,应该调用 onFulfilled, 参数是value
    2.2. 在promise变成 fulfilled 之前, 不应该被调用.
    2.3. 它肯定不能被调用屡次。
  3. 如果onRejected是一个函数
    2.1. 在promise变成 rejected 时,应该调用 onRejected, 参数是reason
    2.2. 在promise变成 rejected 之前, 不应该被调用.
    2.3. 它肯定不能被调用屡次。
  4. 在执行上下文栈中只蕴含平台代码之前,onFulfilled或onRejected肯定不能被调用
  5. 同一个promise上的then可能被调用屡次
    6.1. 如果promise被解决,所有相应的onFulfilled回调必须依照他们原始调用then的程序执行
    6.2. 如果promise被回绝,所有相应的onRejected回调必须依照他们原始调用then的程序执行
  6. then必须返回一个promise对象
promise2 = promise1.then(onFulfilled,onRejected)

resolvePromise

resolvePromise(promise2, x, resolve, reject)
  1. 如果promise和x援用同一个对象,用一个TypeError作为起因来回绝promise
  2. 如果x是一个promise,依据x的状态:
    2.1. 如果x是pending,promise必须放弃期待状态,直到x被解决或回绝
    2.2. 如果x是fulfilled,用雷同的value解决promise
    2.3. 如果x是rejected,用雷同的reason回绝promise
  3. 否则,如果x是一个对象或函数

    3.1.让then成为x.then

    let then = x.then.

    3.2. 如果检索属性x.then导致抛出了一个异样e,用e作为起因回绝promise
    3.3. 如果then是一个函数,用x作为this调用它。then办法的参数为俩个回调函数,第一个参数叫做resolvePromise,第二个参数叫做rejectPromise:

     3.3.1. 如果resolvePromise用一个值y调用,运行[[Resolve]](promise, y)。译者注:这里再次调用[[Resolve]](promise,y),因为y可能还是promise 3.3.2. 如果rejectPromise用一个起因r调用,用r回绝promise。译者注:这里如果r为promise的话,依旧会间接reject,回绝的起因就是promise。并不会等到promise被解决或回绝 3.3.3. 如果`resolvePromise`和`rejectPromise`都被调用,或者对同一个参数进行屡次调用,那么第一次调用优先,当前的调用都会被疏忽。译者注:这里次要针对thenable,promise的状态一旦更改就不会再扭转。 3.3.4. 如果调用then抛出了一个异样e,     3.3.4.1 如果`resolvePromise`或`rejectPromise`曾经被调用,疏忽它     3.3.4.2 否则,用`e`作为起因回绝promise

    3.4. 如果then不是一个函数,用x解决promise

手写Promise

const PENDING = "pending";const FULFILLED = "fulfilled";const REJECTED = "rejected";/** * 1. 构建Promsie类 * 2. 定义3种状态类型 * 3. 设置初始状态 * 4. resolve和reject办法 * 5. 定义结构参数 *  5.1 入参是一个函数, 函数接管resolve和reject两个参数. *  5.2 留神在初始化promise的时候, 就要执行这个函数, 并且有任何报错都要通过reject抛出去 * 6. 实现then办法 */class MPromise {  FULFILLED_CALLBACK_LIST = [];  REJECTED_CALLBACK_LIST = [];  _status = PENDING;  constructor(fn) {    // this.status = PENDING;    this.value = null;    this.reason = null;    try {      fn(this.resolve.bind(this), this.reject.bind(this));    } catch (error) {      this.reject(e.message);    }  }  get status() {    return this._status;  }  set status(newStatus) {    this._status = newStatus;    switch (newStatus) {      case FULFILLED: {        this.FULFILLED_CALLBACK_LIST.forEach((callback) => {          callback(this.value);        });        break;      }      case REJECTED: {        this.REJECTED_CALLBACK_LIST.forEach((callback) => {          callback(this.reason);        });        break;      }    }  }  resolve(value) {    if (this.status === PENDING) {      this.value = value;      this.status = FULFILLED;    }  }  reject(reason) {    if (this.status === PENDING) {      this.reason = reason;      this.status = REJECTED;    }  }  then(onFulfilled, onRejected) {    const realOnFulfilled = this.isFunction(onFulfilled)      ? onFulfilled      : (value) => value;    const realOnRejected = this.isFunction(onRejected)      ? onRejected      : (reason) => {          throw reason;        };    const promise2 = new MPromise((resolve, reject) => {      const fulfilledMicrotask = () => {        queueMicrotask(() => {          try {            const x = realOnFulfilled(this.value);            this.resolvePromise(promise2, x, resolve, reject);          } catch (e) {            reject(e);          }        });      };      const rejectedMicrotask = () => {        queueMicrotask(() => {          try {            const x = realOnRejected(this.reason);            this.resolvePromise(promise2, x, resolve, reject);          } catch (e) {            reject(e);          }        });      };      switch (this.status) {        case FULFILLED: {          fulfilledMicrotask();          break;        }        case REJECTED: {          rejectedMicrotask();          break;        }        case PENDING: {          this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask);          this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask);        }      }    });    return promise2;  }  resolvePromise(promise2, x, resolve, reject) {    // 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise    if (promise2 === x) {      return reject(        new TypeError("The promise and the return value are the same")      );    }    if (x instanceof MPromise) {      // 如果 x 为 Promise ,则使 newPromise 承受 x 的状态      // 也就是继续执行x,如果执行的时候拿到一个y,还要持续解析y      queueMicrotask(() => {        x.then((y) => {          this.resolvePromise(promise2, y, resolve, reject);        }, reject);      });    } else if (typeof x === "object" || this.isFunction(x)) {      // 如果 x 为对象或者函数      if (x === null) {        // null也会被判断为对象        return resolve(x);      }      let then = null;      try {        // 把 x.then 赋值给 then        then = x.then;      } catch (error) {        // 如果取 x.then 的值时抛出谬误 e ,则以 e 为据因回绝 promise        return reject(error);      }      // 如果then是函数      if (this.isFunction(then)) {        let called = false;        try {          then.call(            x,            // 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise            (y) => {              // 须要有一个变量called来保障只调用一次.              if (called) return;              called = true;              this.resolvePromise(promise2, y, resolve, reject);            },            // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 回绝 promise            (r) => {              if (called) return;              called = true;              reject(r);            }          );        } catch (error) {          // 如果调用 then 办法抛出了异样 e:          if (called) return;          // 否则以 e 为据因回绝 promise          reject(error);        }      } else {        resolve(x);      }    } else {      resolve(x);    }  }  static resolve(value) {    if (value instanceof MPromise) {      return value;    }    return new MPromise((resolve) => {      resolve(value);    });  }  static reject(reason) {    return new MPromise((resolve, reject) => {      reject(reason);    });  }  static race(promiseList) {    return new MPromise((resolve, reject) => {      const length = promiseList.length;      if (length === 0) {        return resolve();      } else {        for (let i = 0; i < length; i++) {          MPromise.resolve(promiseList[i]).then(            (value) => {              return resolve(value);            },            (reason) => {              return reject(reason);            }          );        }      }    });  }  static all(promiseList) {    let results = [];    let promiseCount = 0;    let promisesLength = promiseList.length;    return new MPromise((resolve) => {      Promise.resolve(val).then(        (res) => {          promiseCount++;          results[i] = res;          if (promiseCount === promisesLength) {            return resolve(results);          }        },        (err) => {          return reject(err);        }      );    });  }  isFunction(param) {    return typeof param === "function";  }}

练习

  1. 求运行后果
const promise = new Promise((resolve, reject) => {  console.log(1)  resolve()  console.log(2) // 这里有纳闷})promise.then(() => {  console.log(3)})console.log(4)

运行后果:

1243
  1. 求运行后果
const promise1 = new Promise((resolve, reject) => {  setTimeout(() => {    resolve('success')  }, 1000)})const promise2 = promise1.then(() => {  throw new Error('error!!!') // 这里不太确定 status}) console.log('promise1', promise1)console.log('promise2', promise2) setTimeout(() => {  console.log('promise1', promise1)  console.log('promise2', promise2)}, 2000)

运行后果

promise1 Promise { <pending> }promise2 Promise { <pending> }(node:50928) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: error!!!(node:50928) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.promise1 Promise { 'success' }promise2 Promise {  <rejected> Error: error!!!    at promise.then (...)    at <anonymous> }
  1. 求运行后果
const promise = new Promise((resolve, reject) => {  resolve('success1')  reject('error')  resolve('success2')}) promise  .then((res) => {    console.log('then: ', res)  })  .catch((err) => {    console.log('catch: ', err)  })

运行后果

then: success1
  1. 求运行后果
Promise.resolve(1)  .then((res) => {    console.log(res)    return 2  })  .catch((err) => {    return 3  })  .then((res) => {    console.log(res)  })

运行后果

12

catch如果胜利执行,会返回雷同的value也就是2.

  1. 求运行后果
const promise = new Promise((resolve, reject) => {  setTimeout(() => {    console.log('once')    resolve('success')  }, 1000)}) const start = Date.now()promise.then((res) => {  console.log(res, Date.now() - start)})promise.then((res) => {  console.log(res, Date.now() - start)})

运行后果

oncesuccess 1009success 1009
  1. 求运行后果
Promise.resolve()  .then(() => {    return new Error('error!!!')  })  .then((res) => {    console.log('then: ', res)  })  .catch((err) => {    console.log('catch: ', err)  })

运行后果

then: Error: error!!!    at Promise.resolve.then (...)    at ...

then的onFulfilled函数外面批改rejected的:

return Promise.reject(new Error('error!!!'))throw new Error('error!!!')
  1. 求运行后果
const promise = Promise.resolve()  .then(() => {    return promise  })promise.catch(console.error)

运行后果

TypeError: Chaining cycle detected for promise #<Promise>    at <anonymous>    at process._tickCallback (internal/process/next_tick.js:188:7)    at Function.Module.runMain (module.js:667:11)    at startup (bootstrap_node.js:187:16)    at bootstrap_node.js:607:3
  1. 求运行后果
Promise.resolve(1)  .then(2)  .then(Promise.resolve(3))  .then(console.log)

运行后果

1

解释:then函数的onFulfilled不为函数,默认为(value)=>value

  1. 求运行后果
Promise.resolve()  .then(function success (res) {    throw new Error('error')  }, function fail1 (e) {    console.error('fail1: ', e)  })  .catch(function fail2 (e) {    console.error('fail2: ', e)  })

运行后果

fail2: Error: error    at success (...)    at ...

解释: resolve函数执行完返回fullfile状态的promsie对象。在第一个then函数的onFullfield抛出异样。则返回promsie对象的状态为rejected。被catch函数捕捉。

  1. 求运行后果
process.nextTick(() => {  console.log('nextTick')})Promise.resolve()  .then(() => {    console.log('then')  })setImmediate(() => {  console.log('setImmediate')})console.log('end')
endnextTickthensetImmediate

解释: process.nextTickpromise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。

小结

promise的实现最难了解的就是resolvePromise,十分的绕。另外学习到的了一个新的APIqueueMicrotask,有些文章是应用setTimeout来模仿实现微工作调用。

setTimeout(() => {},0)

参考文章

  • Promises/A+
  • Promise/A+ 标准(译)
  • Promise.all手动实现