乐趣区

关于promise:Promise总结

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)

运行后果:

1
2
4
3
  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)
  })

运行后果

1
2

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)
})

运行后果

once
success 1009
success 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')
end
nextTick
then
setImmediate

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

小结

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

setTimeout(() => {},0)

参考文章

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