likes
comments
collection
share

带你手写promise的整个逻辑

作者站长头像
站长
· 阅读数 67

想要手写一个promise,首先就要了解promise,想必大家都被问过一些promise的面试题,知道一些promise的用法,主要考的就是一种异步编程的思想。

本篇文章主要实现promise的thencatchfinally方法及链式调用值穿透等。通过推导的方式实现较为完整的Promise。

Promise-结构的设计

promise的特性

  1. 创建promise实例时,传入的回调函数(executer)会被同步执行
  2. promise存在三种状态,分别是pending、fulfilled、rejected
  3. pending(等待态)为初始态,成功时会转化为fulfilled,失败时会转化rejected,状态一旦决议就无法改变
  4. 如果传入的executor报错,就会直接执行reject
// 定义三种状态的常量
const PROMISE_STATUS_PENDING = "pending"; // 等待状态
const PROMISE_STATUS_FULFILLED = "fulfilled"; // 成功状态
const PROMISE_STATUS_REJECTED = "rejected"; // 失败状态

class MYPromise {
  constructor(executer) {
    this.status = PROMISE_STATUS_PENDING; // 初始态为pending
    this.value = undefined; // 成功的值
    this.reason = undefined; // 失败的值

    const resolve = (value) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        this.status = PROMISE_STATUS_FULFILLED; // 确认成功状态
        this.value = value;
      }
    };

    const reject = (reason) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        this.status = PROMISE_STATUS_REJECTED; // 确认失败状态
        this.reason = reason;
      }
    };
    
    try {
      executer(resolve, reject); // 创建实例时直接执行
    } catch (error) {
      reject(error); // 如果传入的executor报错,就会直接执行reject
    }
  }
}

Promise-then方法

then方法的基本实现

promise中有一个then方法,里面有两个参数:onFulfilled(成功的回调)和onRejected(失败的回调)

  • 当状态为fulfilled时,将this.value的值传入onFulfilled执行。当状态为rejected时,将this.reason的值传入onRejected执行
  • 由于onFulfilled和和onRejected要等this.value和this.reason有值了再执行,所以resolve函数和reject函数内部应该异步执行,我这边使用queueMicrotask(原生api,传入的回调函数会当作微任务执行)
  • 由于promise实例在多处都会使用,所以可以通过数组对onFulfilled和和onRejected进行保存
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

class MYPromise {
  constructor(executer) {
    this.status = PROMISE_STATUS_PENDING;
    this.value = undefined;
    this.reason = undefined;

    this.onFulfilledFns = []; // 对onFulfilled进行保存
    this.onRejectedFns = []; // 对onRejected进行保存

    const resolve = (value) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        queueMicrotask(() => { // 先将成功的回调存入this.onFulfilledFns中再挨个执行,使用微任务的方式
          this.status = PROMISE_STATUS_FULFILLED;
          this.value = value;
          this.onFulfilledFns.forEach((fn) => fn(value));
        });
      }
    };

    const reject = (reason) => {
      if (this.status === PROMISE_STATUS_PENDING) { 
        queueMicrotask(() => { // 先将失败的回调存入this.onRejectedFns中再挨个执行,使用微任务的方式
          this.status = PROMISE_STATUS_REJECTED;
          this.reason = reason;
          this.onRejectedFns.forEach((fn) => fn(reason));
        });
      }
    };
    
    try {
      executer(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  then(onFulfilled, onRejected) {
    this.onFulfilledFns.push(onFulfilled); // 存入onFulfilledFns
    this.onRejectedFns.push(onRejected); // 存入onFulfilledFns
  }
}

解决在宏任务中执行promise.then的问题

上面已经完成对.then方法的基本实现,但是会遇到一个问题。

问题:如果把promise放到setTimeout中执行,由于宏任务会等微任务执行完后再执行,那么.then方法中的回调函数并不会加入到this.onFulfilledFns和this.onRejectedFns中。

解决方法:由于在setTimeout中状态已经决议完成,那么可以通过判断状态如果是fulfilled就执行成功的回调,是rejected就执行失败的回调,无须存在数组中。

  then(onFulfilled, onRejected) {
    if (this.status === PROMISE_STATUS_FULFILLED) { // 如果是fulfilled就执行成功的回调(考虑setTimeout)
      onFulfilled(this.value); 
    }
    if (this.status === PROMISE_STATUS_REJECTED) { // 如果rejected就执行失败的回调(考虑setTimeout)
      onRejected(this.reason);
    }
    if (this.status === PROMISE_STATUS_PENDING) {
      this.onFulfilledFns.push(onFulfilled); // 存入onFulfilledFns
      this.onRejectedFns.push(onRejected); // 存入onFulfilledFns
    }
  }

实现promise的链式调用及值穿透

在开发中我们经常用到new Promise().then().then(),这就是链式调用,用于解决回调地狱

  • 为了达成链式,可以将then里的返回值为promise,通过resolve和reject值穿透到下一个promise中。
  then(onFulfilled, onRejected) {
    return new MYPromise((resolve, reject) => {
      // 返回promise实现链式
      if (this.status === PROMISE_STATUS_FULFILLED) {
        try {
          const value = onFulfilled(this.value); // 拿到返回值
          resolve(value); // 传递到下一个promise中,下同
        } catch (error) {
          reject(error);
        }
      }
      if (this.status === PROMISE_STATUS_REJECTED) {
        try {
          const reason = onRejected(this.reason);
          reject(reason);
        } catch (error) {
          reject(error);
        }
      }
      if (this.status === PROMISE_STATUS_PENDING) {
      
        if (onFulfilled) {
          this.onFulfilledFns.push(() => {
            // 通过回调的方式存在数组中,以实现值传递,下同
            try {
              const value = onFulfilled(this.value);
              resolve(value);
            } catch (error) {
              reject(error);
            }
          });
        }
        if (onRejected) {
          this.onRejectedFns.push(() => {
            try {
              const reason = onRejected(this.reason);
              reject(reason);
            } catch (error) {
              reject(error);
            }
          });
        }
      }
    });
  }

Promise-catch方法

Promise对象的 catch()  方法用于注册一个在 promise 被拒绝时调用的函数。它会立即返回一个等价的 Promise对象,这可以允许你链式调用其他 promise 的方法。

实现思路

  • catch() 方法内部会调用当前 promise 对象的 then() 方法,并将 undefined 和 onRejected作为参数传递给 then()
  • 如果then中没有传入onRejected,那么默认给onRejected一个抛异常的函数,以传递到下一个catch
  then(onFulfilled, onRejected) {
    // 如果onRejected没有传入,就将抛异常给catch中
    onRejected =
      onRejected ||
      ((err) => {
        throw err;
      });
    return new MYPromise((resolve, reject) => {
       ......
    });
  }
  catch(onRejected) {
    const reason = this.then(undefined, onRejected);
    return reason;
  }

Promise-finally方法

finally()  方法返回一个 Promise。在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。

实现思路

  • finally方法内部调用promise对象的then()方法,并将onFinally作为成功和失败回调参数传递给then(),.then(onFinally, onFinally)
  • 但是会遇到一个问题,如果执行完then方法是fulfilled会到catch中,由于catch中onfulfilled是undefined,所以会导致断层。这边解决方案是如果onFulfilled没有值,就默认给一个返回值和参数一样的函数。
  then(onFulfilled, onRejected) {
    onRejected =
      onRejected ||
      ((err) => {
        throw err;
      });
    // 如果是fulfilled执行完then方法,会到catch中,但是为了避免断层(因为catch中onfulfilled是undefined)
    onFulfilled =
      onFulfilled ||
      ((value) => {
        return value;
      });
    return new MYPromise((resolve, reject) => {
        ......
    });
  }
  catch(onRejected) {
    const reason = this.then(undefined, onRejected);
    return reason;
  }
  finally(onFinally) {
    this.then(onFinally, onFinally);
  }

完整代码

// 定义三种状态的常量
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

class MYPromise {
  constructor(executor) {
    this.status = PROMISE_STATUS_PENDING;
    this.value = undefined;
    this.reason = undefined;
    // 为了支持promise多次使用,将成功和失败的回调保存到[]中
    this.onFulfilledFns = [];
    this.onRejectedFns = [];

    const resolve = (value) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        // 放到微任务,确保可以拿到onFulfilled
        queueMicrotask(() => {
          if (this.status !== PROMISE_STATUS_PENDING) return;
          this.status = PROMISE_STATUS_FULFILLED;
          this.value = value;
          this.onFulfilledFns.forEach((fn) => fn(value));
        });
      }
    };

    const reject = (reason) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        // 放到微任务,确保可以拿到onRejected
        queueMicrotask(() => {
          if (this.status !== PROMISE_STATUS_PENDING) return;
          this.status = PROMISE_STATUS_REJECTED;
          this.reason = reason;
          this.onRejectedFns.forEach((fn) => fn(reason));
        });
      }
    };

    try {
      executor(resolve, reject); // 执行回调函数
    } catch (err) {
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    // 如果onRejected没有传入,就将抛异常给catch中
    onRejected =
      onRejected ||
      ((err) => {
        throw err;
      });

    // 如果是fulfilled执行完then方法,会到catch中,但是为了避免断层(因为catch中onfulfilled是undefined)
    onFulfilled =
      onFulfilled ||
      ((value) => {
        return value;
      });

    // 返回promise为了能够达到链式调用与值穿透
    return new MYPromise((resolve, reject) => {
      // 异步使用promise的判断
      if (this.status === PROMISE_STATUS_FULFILLED) {
        try {
          const value = onFulfilled(this.value); // 拿到返回值
          resolve(value); // 传递到下一个promise中,下同
        } catch (error) {
          reject(error);
        }
      }
      if (this.status === PROMISE_STATUS_REJECTED) {
        try {
          const reason = onRejected(this.reason);
          reject(reason);
        } catch (error) {
          reject(error);
        }
      }
      //  同步使用promise的判断:成功的回调和失败的回调保存到数组中
      if (this.status === PROMISE_STATUS_PENDING) {
        // 通过push回调函数的方式,实现同步promise的值穿透及链式调用
        if (onFulfilled) {
          this.onFulfilledFns.push(() => {
            try {
              const value = onFulfilled(this.value);
              resolve(value);
            } catch (error) {
              reject(error);
            }
          });
        }

        if (onRejected) {
          this.onRejectedFns.push(() => {
            try {
              const reason = onRejected(this.reason);
              reject(reason);
            } catch (error) {
              reject(error);
            }
          });
        }
      }
    });
  }

  catch(onRejected) {
    const reason = this.then(undefined, onRejected);
    return reason;
  }

  finally(onFinally) {
    this.then(onFinally, onFinally);
  }
}

结尾

本文手写promise的内容,参考大神coderwhy老师讲解的手写promise的整个逻辑。

转载自:https://juejin.cn/post/7222310732293570619
评论
请登录