【杂记】Promise 基础概念和手写静态方法

139 阅读6分钟

Promise 基础概念

  1. 为了解决异步函数的回调地狱而产生,可以对异步函数进行链式调用
  2. 有三个状态 pending fulfilled rejected, 只能是 pending 向其他状态转,而且只能转换一次
  3. 传参: (resolve, reject) => {}, 一个匿名函数或者箭头函数,可以用两个参数对 Promise 的状态进行变更,然后返回一个 Promise
  4. Promise 内部的异常会被捕获,不被外部代码获取,promise 对象的状态也会变为 rejected
  5. Promise 新建后会立即执行,并且调用 resolve 和 reject 并不会终止 Promise 的参数函数执行

原型方法

Promise.prototype.then()

两个回调函数,第一个 resolved 状态执行,第二个 rejected 状态执行,都返回 promise 对象

Promise.prototype.catch()

一个回调函数,只有 rejected 状态执行,返回 promise 对象

promise 抛出的错误会层层冒泡的传递下去,而且如果是 resolved 状态,会跳过 .catch 。所以最好不要用 .then 的第二个参数,而是使用 .catch 跟在 .then 的后面,这样就能捕获 .then 代码中的报错,

备注:promise 对象抛出的错误不会传递到外层代码,即不会有任何反应

// 手写代码
Promise.prototype.catch = function (callback) {
  return this.then(null, callback);
};

Promise.prototype.finally()

任何状态都会执行回调, 而且 finaly 方法总是返回原来的值

// 手写代码
Promise.prototype.finally = function (callback) {
  let p = this.constructor;
  return this.then(
    (value) => p.resolve(callback()).then(() => value),
    (reason) =>
      p.resolve(callback()).then(() => {
        throw reason;
      })
  );
};

静态方法

前置知识点:Iterable(可迭代) 对象

  • 包括 Array Map Set String arguments NodeList 等
  • 拥有 [Symobel.iterator] 方法, 包含它的对象被认为是可迭代的 iterator
  • 所有可迭代对象可供 for of 消费
  • Iterator 遍历过程
    1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质就是一个指针对象
    2. 第一次调用指针对象的 next 方法,可以将指针指向数据结构的第一个成员
    3. 第二次调用指针对象的 next 方法,指针就指向数据结构的第二个成员
    4. 不断的调用指针对象的 next 方法,直到它指向数据结构的结束位置
  • 每一次调用 next 方法,都会返回一个包含 value 和 done 两个属性的值
    • value 当前成员的值
    • done 布尔值 表示遍历是否结束
  • 使用 TypeScript 的写法, 遍历器接口(Iterable), 指针对象(Iterator)和 next 方法定义如下
    interface Iterable {
      [Symbol.iterator](): Iterator;
    }
    
    interface Iterator {
      next(value?: any): IterationResult;
    }
    
    interface IterationResult {
      value: any;
      done: boolean;
    }
    

Promise.resolve()

将现有对象封装成 promise 对象, 返回值有下面 4 种情况

  • 参数为 Promise 对象,则原封不动返回该对象
  • 参数为空,返回状态为 fulfilled 的 promise 对象
  • 参数为 thenble 对象,即具有 then 方法的对象,则将该对象转为 promise 然后立即执行 then 方法
    let thenble = {
      then: function (resolve, reject) {
        resolve(42);
      },
    };
    let p = Promise.resolve(thenble);
    p.then((value) => console.log(value)); // 42
    
  • 参数为非 promise、非 thenble 对象,则返回 fulfilled 状态的 promise 对象,值为该参数
// 手写代码
Promise.resolve = function (value) {
  if (value instanceof Promise) {
    return value;
  }
  return new Promise((resolve, reject) => resolve(value));
};

Promise.reject()

返回一个 rejected 状态的 Promise 对象,值为传入的参数

// 手写代码
Promise.reject = function (value) {
  return new Promise((resolve, reject) => reject(value));
};

Promise.all()

用于多个 promise, 包装成一个新的 Promise 实例

全部成功才成功返回,一个失败就直接返回失败

  • 参数:Iterable 对象
  • 返回值
    • 全部成功,则是 resolved 状态的 promise, 值为各个 promise 的结果数组,顺序为传参顺序
    • 一个失败,reject 状态的 promise, 值为 失败的 promise 的 reason
    • 可迭代对象为 0, resolved 状态的 promise
// 手写代码
Promise.all = function (args) {
  return new Promise((resolve, reject) => {
    // iterator 对象计数器
    let iteratorIndex = 0;
    // 完成 resolve 的数量
    let index = 0;
    const promiseResults = [];
    // 拥有迭代器接口的可以使用 for of
    for (const item of args) {
      // for of 进行遍历,用于返回正确顺序的结果
      let resultIndex = iteratorIndex;
      iteratorIndex++;
      // 包一层, 兼容非 Promise 对象
      Promise.resolve(item)
        .then((value) => {
          promiseResults[resultIndex] = value;
          index++;
          // 迭代器对象不能单纯的用 length 或是 size 来进行判断
          if (index === iteratorIndex) {
            resolve(promiseResults);
          }
        })
        .catch((reason) => reject(reason));
    }
    // 处理空 iterator 情况
    if (iteratorIndex === 0) {
      resolve([]);
    }
  });
};

Promise.race()

同样是将多个 Promise 实例,包装成一个新的 Prmise 实例

谁最先返回就返回谁

  • 传参:Iterable 对象
  • 返回值:最先改变状态的 promise 实例的返回值
// 手写代码
Promise.race = function (args) {
  return new Promise((resolve, reject) => {
    for (let item of args) {
      Promise.resolve(item)
        .then((value) => resolve(value))
        .catch((reason) => reject(reason));
    }
  });
};

Promise.allSettled()

将多个 Promise 实例,包装成一个新的 Prmise 实例

大家都返回

  • 传参:Iterable 对象, 每个成员都需要是 promise 实例(MDN 这么说明的,实测不是也没关系)
  • 返回值:按照传参顺序返回包含所有结果的数组,成功为 {value, status: 'fulfilled'}, 失败为 {reason, status: 'rejected'}
// 手写代码
Promise.allSettled = function (args) {
  return new Promise((resolve, reject) => {
    const promiseResults = [];
    let iteratorIndex = 0;
    let fulfillIndex = 0;

    const addResult = (result, i) => {
      fulfillIndex++;
      promiseResults[i] = result;
      if (fulfillIndex === iteratorIndex) {
        resolve(promiseResults);
      }
    };
    for (let item of args) {
      let resultIndex = iteratorIndex;
      iteratorIndex++;
      Promise.solve(item)
        .then((value) => addResult({ status: "fulfilled", value }, resultIndex))
        .catch((reason) => addResult({ status: "rejected", reason }, resultIndex));
    }
    if (iteratorIndex === 0) {
      resolve([]);
    }
  });
};

Promise.any()

接受一组 Promise 实例作为参数,包装成一个新的 Promise 返回

全部失败才返回失败,一个成功就返回成功

  • 参数:Iterable 对象,
  • 返回值:
    • 某个 promise 状态变为 fulfilled,则返回该 promise 的返回值,不再等后面的 promise 执行
    • 所有的状态都变为 rejected, 则返回一个 AggregateError 实例,
      • AggregateError 继承于 Error
      • AggregateError 实例属性 errors 包含了每一个 rejected 抛出的错误
      try {
        throw new AggregateError([new Error("some error")], "Hello");
      } catch (e) {
        console.log(e instanceof AggregateError); // true
        console.log(e.message); // "Hello"
        console.log(e.name); // "AggregateError"
        console.log(e.errors); // [ Error: "some error" ]
      }
      

使用示例:

const resolved = Promise.resolve(1);
const rejected = Promise.reject(2);
const alsoRejected = Promise.reject(3);

Promise.any([resolved, rejected, alsoRejected]).then((value) => console.log(value)); // 1

Promise.any([rejected, alsoRejected]).catch((reason) =>
  console.log(reason.errors, reason instanceof AggregateError)
); // [2, 3] true

总结

  • 核心概念
    • promise 一直会返回新的 promise 实例,以实现链式调用,用来解决异步函数的回调地狱
    • 三个状态 pending fulfilled rejected
    • 状态只能从 Pending 到 fulfilled 或者 rejected, 且一但改变就不能更改
  • 原型方法
    • .then
      • 接受两个回调,分别是成功和失败的回调
    • .catch
      • 只接受失败的回调,如果不是 rejected 状态则直接跳过
    • .finally
      • 无论什么状态都执行回调,返回值与上一步返回值一模一样
  • 静态方法
    • Promise.all 全成功则返回成功, value 为传参顺序的数组,任何一个失败则返回失败
    • Promise.race 哪个最先返回则返回它
    • Promise.any 任何一个成功则直接返回,全失败则失败,reason 为 AgregrateError 对象, errors 属性里包含所有的 reason.
    • Promise.allSettled 全部返回,成功 {status: 'fulfilled', value} 失败 {stauts: 'rejected', reason}
    • Promise.resolve 参数为 promise 则返回该 promise, 否则返回成功的 promise, value 为传参
    • Promise.reject 返回一个失败的 promise, reason 为传参