手写Promise

原理面试

# 前言

手写了个MyPromise类实现了基本的Promise功能,包括promise的创建、链式回调、thencatchPromise.resolvePromise.rejectPromise.allPromise.race

主要看代码和注释吧

# 定义类和构造器

  1. Promise的三种状态:pending进行中、fulfilled已完成、rejected已拒绝,用status存储
  2. Promise的执行器函数的传参放放于value中,用于异步操作后返回的结果。
  3. 两个队列分别存储成功和失败的回调函数,用于异步操作后取出执行回调。
class MyPromise {
  // 定义Promise的三种状态
  static PENDING = 'pending'; // 进行中
  static FULFILLED = 'fulfilled'; // 已完成
  static REJECTED = 'rejected'; // 已拒绝

  value = undefined;
  status = MyPromise.PENDING;
  onFulFilledCallbacks = []; // 成功回调队列
  onRejectedCallbacks = []; // 失败回调队列

  /**
   * 构造函数,接受一个执行器函数作为参数,用于异步操作。
   * @param {Function} executor 执行器函数,接受resolve和reject两个参数。
   */
  constructor(executor) {
    const resolve = value => {
      // 当状态为PENDING时,改变状态为FULFILLED,取出成功回调队列一一执行
      if (this.status === MyPromise.PENDING) {
        this.status = MyPromise.FULFILLED;
        this.value = value;
        this.onFulFilledCallbacks.forEach(fn => fn(value));
      }
    };
    const reject = value => {
      // 当状态为PENDING时,改变状态为REJECTED,取出失败回调队列一一执行
      if (this.status === MyPromise.PENDING) {
        this.status = MyPromise.REJECTED;
        this.value = value;
        this.onRejectedCallbacks.forEach(fn => fn(value));
      }
    };

    try {
      executor(resolve, reject); // 执行传入的执行器函数
    } catch (error) {
      reject(error); // 如果执行器函数抛出错误,则调用reject
    }
  }
}

# then

promise的关键then方法,接收两个回调函数,去处理promise中同步或异步操作的结果
为了能够链式调用,then返回的也会是一个promise

queueMicrotask (opens new window)接收一个回调,让回调在微任务队列中执行。看到一些封装用的是setTimeout,但是setTimeout是宏任务,这样执行顺序会和promise有些区别了。

  /**
   * then方法用于注册成功和失败的回调函数。
   * @param {Function} onFulFilled 成功回调函数
   * @param {Function} onRejected 失败回调函数
   * @returns {MyPromise} 返回一个新的MyPromise实例
   */
  then(onFulFilled, onRejected){
    // 如果传参不是函数,则封装成函数 返回其默认值/抛出异常
    onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : (value) => value;
    onRejected = typeof onRejected === 'function' ? onRejected : (value) => {throw value};
    return new MyPromise((resolve, reject)=>{ // 返回一个新的MyPromise实例
      if (this.status === MyPromise.PENDING) {
        // 当状态为PENDING时,说明执行器函数中传入的resolve/reject函数还未执行,即是函数是异步调用
        // 将成功和失败的回调分别放入前面定义的回调队列中,等待异步任务执行到resolve/reject函数调用后,上面定义的resolve/reject函数会将回调队列取出执行
        this.onFulFilledCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              const result = onFulFilled(this.value);
              this.handlePromiseResult(result, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
        this.onRejectedCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              const result = onRejected(this.value);
              this.handlePromiseResult(result, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
      } else if (this.status === MyPromise.FULFILLED) {
        // 当状态为FULFILLED时,说明resolve已经执行,则执行成功回调
        queueMicrotask(() => {
          try {
            const result = onFulFilled(this.value);
            this.handlePromiseResult(result, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.status === MyPromise.REJECTED) {
        // 当状态为REJECTED时,执行失败回调
        queueMicrotask(() => {
          try {
            const result = onRejected(this.value);
            this.handlePromiseResult(result, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }
    });
  }

promise是能进行链式调用的,所有需要对回调的返回值做一些处理,

  /**
   * 处理promise链式调用的结果。
   * @param {any} result then返回的结果
   * @param {Function} resolve 解决新promise的方法
   * @param {Function} reject 拒绝新promise的方法
   */
  handlePromiseResult(result, resolve, reject){
    if (result instanceof MyPromise) {
      // 如果结果是MyPromise实例,则递归处理
      result.then(resolve, reject);
    } else {
      // 否则,直接解决新promise
      resolve(result);
    }
  }

# catch

用于注册失败的回调函数,其实就是then方法的语法糖

  /**
   * catch方法用于注册失败的回调函数
   * @param {Function} onRejected 失败回调函数
   * @returns {MyPromise} 返回一个新的MyPromise实例
   */
  catch(onRejected){
    return this.then(null, onRejected);
  }

# Promise.resolve

  /**
   * 静态方法,创建一个已解决的promise。
   * @param {any} value 解决的值
   * @returns {MyPromise} 返回一个新的已解决的MyPromise实例
   */
  static resolve(value){
    return new MyPromise(resolve => {
      resolve(value);
    });
  }

# Promise.reject

  /**
   * 静态方法,创建一个已拒绝的promise。
   * @param {any} value 拒绝的值
   * @returns {MyPromise} 返回一个新的已拒绝的MyPromise实例
   */
  static reject(value){
    return new MyPromise((_, reject) => {
      reject(value);
    });
  }

# Promise.all

有个点需要注意的是,返回值的顺序需和传入的promise的顺序一致,而不是执行顺序,所以需要借助count去记录完成的数量,而不能直接用index来判断


  /**
   * 静态方法,接收一个promise数组,当所有promise都解决时,返回一个解决的promise。
   * @param {Array} promiseArr promise数组
   * @returns {MyPromise} 返回一个新的MyPromise实例,当所有输入的promise都解决时,它将被解决
   */
  static all(promiseArr){
    return new MyPromise((resolve, reject) => {
      let result = []; // 存储解决的值
      let count = 0; // 记录已解决的promise数量
      promiseArr.forEach((promise, index) => {
        promise.then(res => {
          result[index] = res;
          count++;
          if (count === promiseArr.length) {
            resolve(result);
          }
        }).catch(err => {
          reject(err);
        });
      });
    });
  }

# Promise.race

  /**
   * 静态方法,接收一个promise数组,当任意一个promise解决或拒绝时,返回结果。
   * @param {Array} promiseArr promise数组
   * @returns {MyPromise} 返回一个新的MyPromise实例,当任意一个输入的promise解决或拒绝时,它将提供相同的结果
   */
  static race(promiseArr){
    return new MyPromise((resolve, reject) => {
      promiseArr.forEach(promise => {
        promise.then(res => {
          resolve(res);
        }).catch(err => {
          reject(err);
        });
      });
    });
  }
Wonderwall
Oasis