手写promise

77 阅读6分钟

是什么:promise 是一个构造函数,本质上是,js对象,用来生成promise实例。
解决什么问题:promise 是用来解决回调地狱的
promise的三个状态:有三种状态pending、fulfilled、reject
promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,他们是两个函数,由js引擎提供,不用自己部署
resolve 函数的作用是,将promise对象的状态从pending变为fulfillied,在异步操作成功时调用,并将一步操作的结果,作为参数传递出去
reject函数的作用,是将Promise对象的状态从pending变成reject,在异步操作失败时调用,并将一步操作报出的错误,作为参数传递出去
promise 实例生成以后,可以用then方法分别指定fulfillied状态和rejected状态的回调函数

promise 方法

实现一个promise,从精简版,一步步实现
class MyPromise {
  /**
   * 手动实现一个promise,包括resolve和reject方法,then方法和reject方法
   * @param {*} executor promise实例化时会传入一个回调,内部立即调用executor
   */
  constructor(executor) {
    this.state = "pending";
    this.value = null;
    this.reason = null;
    this.onFulfilledCb = [];
    this.onRejectCb = [];
    const resolve = () => {};
    const reject = () => {};
    executor(resolve, reject);
  }

  then() {}
  catch() {}
}
完善初始化数据  resolve和reject方法
class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = null;
    this.reason = null;
    // 用于存储then方法中,成功和失败的回调
    this.onFulfilledCb = [];
    this.onRejectCb = [];
    // 以下两个函数用于在成功或失败时,更新状态和值(原因),并调用所有注册的回调函数
    const resolve = (value) => {
      if (this.state === "pending") {
        this.state = "fulfilled";
        this.value = value;
        this.onFulfilledCb.forEach((cb) => {
          cb(this.value);
        });
      }
    };
    const reject = (reason) => {
      if (this.state === "pending") {
        this.state = "reject";
        this.reason = reason;
        this.onRejectCb((cb) => {
          cb(this.reason);
        });
      }
    };
    executor(resolve, reject);
  }

  then() {}
  catch() {}
}

promise.then()方法

class MyPromise2 {
  constructor(executor) {
    this.state = "pending"; // fulfilled reject
    this.value = null; // 成功的值
    this.reason = null; // 失败的值
    this.onFulfilledCb = [];
    this.onRejectCb = [];

    const resolve = (value) => {
      if (this.state === "pending") {
        this.state = "fulfilled";
        this.value = value;
        this.onFulfilledCb.forEach((cb) => {
          cb(this.value);
        });
      }
    };
    const reject = (reason) => {
      if (this.state === "pending") {
        this.state = "reject";
        this.reason = reason;
        this.onRejectCb((cb) => {
          cb(this.reason);
        });
      }
    };
    executor(resolve, reject);
  }

/*`promise`调用`then`方法时,当前的`promise`并没有成功,一直处于`pending`状态,
* 所以如果当调用 `then`方法时,当前状态是`pending`,需要`先将成功和失败的回调分别存放起来`,
* 在`executor()`的异步任务被执行时,`触发resolve或reject,依次调用成功或失败的回调`
*/

  // then 方法分别指定fulfilled和rejected状态的回调函数
  then(onFulfilled, onReject) {
    // 同步
    if (this.state === "fulfilled") {
      onFulfilled(this.value);
    }
    if (this.state === "reject") {
      onReject(this.reason);
    }
    // 异步订阅
    if (this.state === "pending") {
      this.onFulfilledCb.push(() => {
        onFulfilled(this.value);
      });
      this.onRejectCb.push(() => {
        onReject(this.reason);
      });
    }
  }
  catch() {}
}

promise.then() 方法链式调用

promise接收两个参数,onFulfilled和onReject
在方法内部如果promise成功或者失败,会立即调用对应的回调函数;
如果是pending,将回调函数添加到回调数组中,并返回一个promise,该对象会在原Promise成功或失败时调用对应的回调函数。


class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = null;
    this.reason = null;
    this.onFulfilledCb = [];
    this.onRejectCb = [];
    const resolve = (value) => {
      if (this.state === "pending") {
        this.state = "fulfilled";
        this.value = value;
        this.onFulfilledCb.forEach((cb) => {
          cb(this.value);
        });
      }
    };
    const reject = (reason) => {
      if (this.state === "pending") {
        this.state = "reject";
        this.reason = reason;
        this.onRejectCb((cb) => {
          cb(this.reason);
        });
      }
    };
    executor(resolve, reject);
  }

  /**
   * 返回一个promise对象,支持链式调用
   * 接收两个可选回调函数,如果成功或失败调用对应的回调函数
   * @param {*} onFulfilled
   * @param {*} onRejected
   * 返回Promise,增加setTimeout延迟代码块执行,不然取不到promise2值会报错
   */
  then(onFulfilled, onReject) {
    let promise2 = new Promise((resolve, reject) => {
      // 如果promise成功或者失败,会立即调用对应的回调函数
      // 如果是pending,将回调函数添加到回调数组中,并返回一个promise,该对象会在原Promise成功或失败时调用对应的回调函数。
      if (this.state === "fulFilled") {
        // 增加setTimeout的原因: 把当前任务放到下一个宏任务里执行。
        // 利用eventLoop 宏任务,让代码块延迟执行,等new完Promise2 ,不然isPromise(Promise2,x,resolve, reject )中取不到Promise2会报错
        // 因为处理异步可能会有问题
        setTimeout(() => {
          try {
            let result = onFulfilled(this.value);
            this.isPromise(promise2, result, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.state === "reject") {
        setTimeout(() => {
          try {
            let result = onReject(this.value);
            this.isPromise(promise2, result, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        this.onFulfilledCb.push(() => {
          // 为代码简洁可读,暂时没有写try catch
          setTimeout(() => {
            const result = onFulfilled(this.value);
            this.isPromise(promise2, result, resolve, reject);
          }, 0);
        });
        this.onRejectCb.push(() => {
          // 为代码简洁可读,暂时没有写try catch
          setTimeout(() => {
            const result = onReject(this.reason);
            this.isPromise(promise2, result, resolve, reject);
          }, 0);
        });
      }
    });
    return promise2;
  }
  
    /**
    promise链式调用。在内部调用then方法时,返回一个新的promise,并让这个新的promise接管了它下一个then方法
     * 前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),
     * 这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用
       x跟promise2不能只一个东西
     * @param {*} promise 
     * @param {*} x 
     * @param {*} resolve 
     * @param {*} reject 
     * @returns 
     */
    function resolvePromise(promise, x, resolve, reject) {
      // 防止内部的循环引用
      if (promise === x) {
        return reject(new TypeError("Chaining cycle detected for promise"));
      }

      // 解析x,判断他是一个promise
      if (x instanceof MyPromise) {
        x.then(resolve, reject);
      } else {
        resolve(x);
      }
    }
  catch() {}
}

promise.all()

Promise.all 方法用于将多个Promise实例,包装成一个新的Promise实例
Promise.all 方法接受一个数组作为参数,p1、p2、p3都是Promise实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转换为Promise实例,再进行一步处理
promise.all状态由P1、P2、P3 决定的,分为两种情况

  • 只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。
  • 只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
const p = Promise.all([]);
/**
 * 返回一个promise对象,让外部可以调用then方法获取结果
 * 主要逻辑遍历arr, 添加结果到数组中
 * @param {*} arr
 * @returns
 */
Promise.all = function (arr) {
  let resultList = []; // 保存所有结果的数组
  let index = 0; // 用于记录操作到第几个项,操作完返回数据
  return new Promise((resolve, reject) => {
    // 1、主要函数逻辑
    for (let i = 0; i < arr.length; i++) {
      // 2、判断当前项,是否为promise,如果是,则调用它的then方法获取返回结果
      if (arr[i] instanceof Promise2) {
        arr[i].then(
          (value) => {
            addData(i, value);
          },
          (reason) => {
            reject(reason);
          }
        );
      } else {
        // 3、如果是普通对象的话,直接返回结果
        addData(i, arr[i]);
      }
    }

    function addData(key, value) {
      resultList[key] = value;
      index++;
      if (index === arr.length) {
        resolve(resultList);
      }
    }
  });
};

promise.resolve 和 promise.reject

// promise.resolve 将现有对象转换为promise对象
Promise.resolve = function (value) {
  if (value instanceof Promise) {
    return value;
  }
  return new Promise((resolve) => {
    resolve(value);
  });
};
// promise.reject 返回一个新的promise实例,该实例的状态为rejected
Promise.reject = function (reason) {
  return new Promise(_, (reject) => reject(reason));
};

Promise.prototype.finally

finally用于不管promise对象最后状态如何,都会执行的操作
调用当前promise的then方法返回一个新的promise对象
调用promise中的resolve方法进行返回

promise.finally(()=>{})
等同于
promise.then((result)=>{return result},(error)=>{ throw error})

Promise.prototype.finally = function (callback) {
  return this.then(
    (value) => {
      this.resolve(callback()).then(() => value);
    },
    (reason) => {
      this.resolve(callback()).then(() => reason);
    }
  );
};

Promise.prototype.catch

Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数

Promise.prototype.catch = function (onReject) {
  return this.then(null, onReject);
};

promise.race

Promise.race = function (arr) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < arr.length; i++) {
      const cur = arr[i];
      Promise.resolve(cur).then(resolve, reject);
    }
  });
};