来跟我手写一遍Promise

289 阅读4分钟

前言

Promise实现以同步的方式书写异步代码,解决了回调地狱的问题。本质上,Promise是观察者模式的一种实现,通过任务注册和状态监听优化了原来的编程模式,增加了代码的可读性和维护性。

手写一遍Promise可以加深我们对于Promise的理解,Promise的核心包括:3个属性,1个构造器、2个实例方法和4个静态方法。

类初始化

首先定义两个属性来保存Promise的状态和结果。

class Promise {
  constructor(executor) {
    this.PromiseState = 'pending' // 存储promise状态
    this.PromiseResult = null // 存储promise结果
  }
}

执行器函数

当实例化Promise时,用户需要传入一个执行器函数executor,执行器函数接收两个内部函数用于改变Promise的状态,所以这一步我们需要:

  1. 定义内部函数:resolve, reject 修改promise状态并保存值。
  2. 调用执行器函数:executor为同步执行,如果内部抛出异常则promise状态为rejected。
class Promise {
  constructor(executor) {
    this.PromiseState = 'pending'; // 存储promise状态
    this.PromiseResult = null; // 存储promise结果

    // 修改promise状态为成功
    const resolve = (data) => {
      // 如果状态已经改变,则不再改变
      if (this.PromiseState !== 'pending') return;
      // 如果状态未改变则改变状态并保存值
      this.PromiseState = 'fulfilled';
      this.PromiseResult = data;
    }

    // 修改promise状态为失败
    const reject = (data) => {
      // 如果状态已经改变,则不再改变
      if (this.PromiseState !== 'pending') return;
      // 如果状态未改变则改变状态并保存值
      this.PromiseState = 'rejected';
      this.PromiseResult = data;
    }

    // 执行器函数为同步执行
    // 如果执行过程中抛出异常则promise状态为失败
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
}

then方法

接下来我们需要定义then方法,用户通过这个方法注册回调函数,当Promise状态改变时,回调将被执行。

  1. then接收两个回调函数作为参数,我们需要校验onResolved,onRejected是否为函数。
  2. then方法返回一个promise,并根据onResolved,onRejected执行结果调整promise状态。
  3. then方法中onResolved,onRejected函数为异步执行。
  4. 如果then执行时promise的状态还未改变,则保存回调函数等待执行,这里需要添加第三个属性callbacks保存then中注册的回调方法。
class Promise {
  constructor(executor) {
    this.PromiseState = 'pending'; // 存储promise状态
    this.PromiseResult = null; // 存储promise结果
    this.callbacks = []; // 保存then中添加的回调方法

    // 修改promise状态为成功
    const resolve = (data) => {
      // 如果状态已经改变,则不再改变
      if (this.PromiseState !== 'pending') return;
      // 如果状态未改变则改变状态并保存值
      this.PromiseState = 'fulfilled';
      this.PromiseResult = data;
      // 执行then中保存下来的回调
      this.callbacks.forEach((item) => {
        item.onResolved();
      });
    };

    // 修改promise状态为失败
    const reject = (data) => {
      // 如果状态已经改变,则不再改变
      if (this.PromiseState !== 'pending') return;
      // 如果状态未改变则改变状态并保存值
      this.PromiseState = 'rejected';
      this.PromiseResult = data;
      // 执行then中保存下来的回调
      this.callbacks.forEach((item) => {
        item.onRejected();
      });
    };

    // 执行器函数为同步执行
    // 如果执行过程中抛出异常则promise状态为失败
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  // then方法
  then(onResolved, onRejected) {
    // 校验onResolved,onRejected是否为函数
    if (typeof onResolved !== 'function') {
      onResolved = value => value
    }
    if (typeof onRejected !== 'function') {
      onRejected = error => {
        throw error
      }
    }
    // then的返回值为一个Promise
    return new Promise((resolve, reject) => {
      // then返回的Promise状态需要根据onResolved和onRejected的返回结果而定
      const callback = (func) => {
        // 捕获函数执行时抛出的异常(由于异步执行,executor处的trycatch无法捕获)
        try {
          const result = func(this.PromiseResult);
          if (result instanceof Promise) {
            result.then(
              (value) => {
                resolve(value);
              },
              (error) => {
                reject(error);
              }
            );
          } else {
            resolve(result);
          }
        } catch(error) {
          reject(error);
        }
      };
      // 如果promise状态已经改变,则异步执行回调
      // 真实情况下会将回调加入微任务队列,这里用宏任务模拟
      if (this.PromiseState === 'fulfilled') {
        setTimeout(() => {
          callback(onResolved);
        });
      }
      if (this.PromiseState === 'rejected') {
        setTimeout(() => {
          callback(onRejected);
        });
      }
      // 如果promise状态还未改变,则先保存
      // 这些方法会在执行器函数中resolve或reject方法执行时执行
      if (this.PromiseState === 'pending') {
        this.callbacks.push({
          onResolved: () => {
            setTimeout(() => {
              callback(onResolved);
            });
          },
          onRejected: () => {
            setTimeout(() => {
              callback(onRejected);
            });
          },
        });
      }
    });
  }
}

catch方法

catch方法是then方法的语法糖,我们定义一下。

  // catch方法(这儿还是定义在Promise类内部,冗余代码过多,后面就不写了)
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

resolve方法

接下来定义几个静态方法,首先是resolve,这个方法用于将数据包装为Promise,如果传入的数据是Promise则直接返回,否则返回一个将入参作为结果的成功状态的Promise

  // resolve方法
  static resolve(data) {
    // 如果传入Promise则直接返回
    if (data instanceof Promise) {
      return data;
    } else {
      return new Promise((resolve) => {
        resolve(data);
      });
    }
  }

reject方法

再来是reject,该方法不论入参是不是Promise,均返回一个失败的Promise,并且值就是传入的数据

  // reject 方法
  static reject(data) {
    return new Promise((_, reject) => {
      reject(data);
    });
  }

all方法

接下来是all,all方法用于判断多个promise是否均为成功,如果多个promise都成功则返回所有传入promise的返回值数组;如果有一个promise失败,则返回该失败的promise的返回值

  // all方法
  static all(promises) {
    let count = 0;
    let arr = [];
    return new Promise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (value) => {
            count++;
            arr[i] = value;
            if (count === promises.length) {
              resolve(arr);
            }
          },
          (error) => {
            reject(error);
          }
        );
      }
    });
  }

race方法

最后是race,该方法用于多个promise竞争执行,如果其中一个promise成功,则返回该成功promise的返回值;如果其中一个promise失败,则返回该失败promise的返回值

  // race方法
  static race(promises) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (value) => {
            resolve(value);
          },
          (error) => {
            reject(error);
          }
        );
      }
    });
  }