前言

平时一直没想过手写 Promise,结果今天笔试初见,绞尽脑汁才写了个简易版本,还是菜了,头被锤烂

其实到了真正手写的时候才发现,自己对 Promise 根本就一无所知。我看你是完全不懂喔

回调函数的执行顺序?怎么实现链式调用?返回值穿透?延迟绑定?resolve、reject 做了什么?then 中的回调函数返回值怎么处理?哪些是同步?哪些是异步?

搞清楚这些问题,才能算是熟悉 Promise了。

代码实现

首先,根据 Promise/A+ 规范,一个 Promise 应该具有三种状态,并且同一时刻只能为其一,状态变更后,不可回退:

const STATUS = {
  PENDING: "pending",
  FULFILLED: "fulfilled",
  REJECTED: "rejected",
};

一开始 Promise 的状态应该是 pending。

先回忆一下 new Promise 的使用:

let p = new Promise((resolve, reject) => {});

它接收一个回调函数 executor,具有两个函数类型的参数:resolve、reject

在构造函数初始化 Promise 实例时,执行 executor,传入构造函数中定义的 resolve、reject 作为其参数。

executor 回调函数中执行 resolve、reject 回调,即可将该 Promise 的状态改变,並异步触发将 then 传入的回调加入微任务队列。因此,用一个回调队列来模拟微任务队列。

value 用来保存 resolve、reject 传入的参数。

class MyPromise {
  constructor(executor) {
    this.status = STATUS.PENDING;
    this.value = null;
    this.resolveCallbacks = [];
    this.rejectCallbacks = [];
    const resolve = (value) => {};
    const reject = (reason) => {};
    try {
      // new Promise()时立即执行executor,并传入resolve和reject
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
}

再想想 resolve、reject 的实现:

仅当 Promise 的状态为 pending 时,可以被调用,改变状态并将参数作为 Promise 的 value

resolve/reject 后,异步执行 then 中传入的回调函数(如果存在多个,依次执行)。

const resolve = (value) => {
  const run = () => {
    if (this.status === STATUS.PENDING) {
      this.status = STATUS.FULFILLED;
      // 储存当前值,用于then回调  
      this.value = value;
      while (this.resolveCallbacks.length) {
        // 一次取一个回调执行 
        const cb = this.resolveCallbacks.shift();
        // 等待该Promise的所有then回调保持一致
        cb(value);
      }
    }
  };
    // 模拟异步,规范上是微任务,这里是宏任务
  setTimeout(run);
};

const reject = (reason) => {
  const run = () => {
    if (this.status === STATUS.PENDING) {
      this.status = STATUS.REJECTED;
      this.value = reason;
      while (this.rejectCallbacks.length) {
        const cb = this.rejectCallbacks.shift();
        cb(value);
      }
    }
  };
  setTimeout(run);
};

接下来是最核心的 then 函数,也是链式调用、返回值穿透、延迟绑定的具体实现:

then 函数本身是同步执行的,而其中的回调函数并不会立即执行,即使在调用 then 注册回调函数的时候 Promise 对象已经是确定的状态,Promise 也会以异步的方式调用该回调函数(延迟绑定)。

如果上一个 Promise 状态不为 pending,将 then 中的回调函数加入微任务队列(这里模拟不了微任务,故用异步宏任务的 resolve/reject 代替,回调入队的逻辑也要发生变化,即 pending 时入队)。在当前宏任务执行完后,依次执行至微任务队列清空。

then 的返回值是一个全新的 Promise(链式调用的原理)。

如果 then 中的回调函数没有返回值,那么 then 返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined

如果 then 中的回调函数返回值为另一个 Promise,等待其状态变更。否则,直接 resolve,并且将返回的值作为接受状态的回调函数的参数值。

如果 then 中的回调函数抛出一个错误,那么 then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。

如果参数不是函数类型,将 value 原封不动地往下传(返回值穿透)。

  // then 函数本身是同步执行
  then(onfulfilled, onrejected) {
    typeof onfulfilled !== "function" ? (onfulfilled = (value) => value) : null;
    typeof onrejected !== "function" ? (onrejected = (error) => error) : null;
    return new MyPromise((resolve, reject) => {
      // 成功回调,resolve 时加入微任务队列  
      const resolveFn = (value) => {
        try {
          const x = onfulfilled(value);
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
        } catch (e) {
          reject(e);
        }
      };
       // 失败回调,reject 时加入微任务队列 
      const rejectFn = (error) => {
        try {
          const x = onrejected(error);
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
        } catch (error) {
          reject(error);
        }
      };
      switch (this.status) {
        case STATUS.PENDING:
          this.resolveCallbacks.push(resolveFn);
          this.rejectCallbacks.push(rejectFn);
          break;
        case STATUS.FULFILLED:
          resolveFn(this.value);
          break;
        case STATUS.REJECTED:
          rejectFn(this.value);
          break;
      }
    });
  }
}

至此,一个基本的 Promise 就完成了:

const STATUS = {
  PENDING: "pending",
  FULFILLED: "fulfilled",
  REJECTED: "rejected",
};
class MyPromise {
  constructor(executor) {
    this.status = STATUS.PENDING;
    this.value = undefined;
    this.resolveCallbacks = [];
    this.rejectCallbacks = [];
    const resolve = (value) => {
      const run = () => {
        if (this.status === STATUS.PENDING) {
          this.status = STATUS.FULFILLED;
          // 储存当前值,用于then回调
          this.value = value;
          // 一次取一个回调执行
          while (this.resolveCallbacks.length) {
            const cb = this.resolveCallbacks.shift();
             // 等待该Promise的所有then回调保持一致
            cb(value);
          }
        }
      };
      // 模拟异步,规范上是微任务,这里是宏任务
      setTimeout(run);
    };
    const reject = (reason) => {
      const run = () => {
        if (this.status === STATUS.PENDING) {
          this.status = STATUS.REJECTED;
          this.value = reason;
          while (this.rejectCallbacks.length) {
            const cb = this.rejectCallbacks.shift();
            cb(value);
          }
        }
      };
      setTimeout(run);
    };
    // new Promise()时立即执行executor,并传入resolve和reject
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  // then函数本身是同步执行
  then(onfulfilled, onrejected) {
    // 根据规范,如果then的参数不是function,则忽略它, 让值继续往下传递,链式调用继续往下执行
    typeof onfulfilled !== "function" ? (onfulfilled = (value) => value) : null;
    typeof onrejected !== "function" ? (onrejected = (error) => error) : null;
    // then 返回一个新的promise
    return new MyPromise((resolve, reject) => {
        // 成功回调,resolve 时加入微任务队列
      const resolveFn = (value) => {
        try {
          const x = onfulfilled(value);
          // 分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
        } catch (e) {
          reject(e);
        }
      };
        // 失败回调,reject 时加入微任务队列 
      const rejectFn = (error) => {
        try {
          const x = onrejected(error);
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
        } catch (error) {
          reject(error);
        }
      };
      // 根据上一个 Promise 当前状态
      switch (this.status) {
        case STATUS.PENDING:
          // 回调函数加入回调任务队列
          this.resolveCallbacks.push(resolveFn);
          this.rejectCallbacks.push(rejectFn);
          break;
        // 模拟中resolve为异步宏任务,所以不会进入
        case STATUS.FULFILLED:
          resolveFn(this.value);
          break;
        case STATUS.REJECTED:
          rejectFn(this.value);
          break;
      }
    });
  }
  catch(rejectFn) {
    return this.then(undefined, rejectFn);
  }
}

然后是一些 Promise 相关的 API:

  static resolve(value) {
    if (value instanceof myPromise) return value;
    if (value === null)
      return new myPromise((resolve, reject) => resolve(null));
    if (typeof value === "object" || typeof value === "function") {
      try {
        // 如果是thenable对象
        let then = value.then;
        if (typeof then === "function") {
          // 返回的promise的状态跟随then执行
          return new myPromise(then.bind(value));
        }
      } catch (e) {
        return new myPromise((resolve, reject) => reject(e));
      }
    }
    return new myPromise((resolve, reject) => resolve(value));
  }
  static reject(reason) {
    return new myPromise((resolve, reject) => {
      reject(reason);
    });
  }
  static finally(callback) {
    return this.then(
      (value) => {
        // callback是异步回调,则等待其执行
        return myPromise.resolve(callback()).then(() => value);
      },
      (error) => {
        return myPromise.reject(callback()).then(() => error);
      }
    );
  }
  static all(promises) {
    return new myPromise((resolve, reject) => {
      // 必须是可迭代对象
      if (
        !promises ||
        typeof promises !== "object" ||
        typeof promises[Symbol.iterator] !== "function"
      ) {
        reject(new TypeError());
      }
      let count = 0;
      const result = [];
      if (promises.length === 0) return resolve([]);
      for (let i = 0; i < promises.length; i++) {
        myPromise.resolve(promises[i]).then(
          (value) => {
            count++;
            result[i] = value;
            if (count === promises.length) {
              resolve(result);
            }
          },
          (error) => reject(error)
        );
      }
    });
  }
  static race(promises) {
    return new myPromise((resolve, reject) => {
      // 必须是可迭代对象
      if (
        !promises ||
        typeof promises !== "object" ||
        typeof promises[Symbol.iterator] !== "function"
      ) {
        reject(new TypeError(""));
      }
      if (promises.length === 0) return;
      for (let i = 0; i < promises.length; i++) {
        myPromise.resolve(promises[i]).then(
          (value) => resolve(value),
          (error) => reject(error)
        );
      }
    });
  }
  static allSettled(promises) {
    return new myPromise((resolve, reject) => {
      // 必须是可迭代对象
      if (
        !promises ||
        typeof promises !== "object" ||
        typeof promises[Symbol.iterator] !== "function"
      ) {
        reject(new TypeError(""));
      }
      if (promises.length === 0) resolve([]);
      let count = 0;
      const result = new Array(promises.length);
      for (let i = 0; i < promises.length; i++) {
        myPromise.resolve(promises[i]).then(
          (value) => {
            result[i] = { status: "fulfilled", value: value };
            count++;
            if (count === promises.length) {
              resolve(result);
            }
          },
          (reason) => {
            result[i] = { status: "rejected", reason: reason };
            count++;
            if (count === promises.length) {
              resolve(result);
            }
          }
        );
      }
    });
  }
}