如何快速默写Promise实现

194 阅读6分钟

背景:如果我们要快速背诵并默写出来promise的实现,应该怎么记忆最快。

在默写实现promise之前,先写一遍promise的基本使用方法,主体,then,catch,all,race,清楚我们要实现什么东西

  1. 主题异步
  2. 多个then,then里面有成功回调和catch回调,有返回值
  3. catch函数
  4. all函数
const p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    console.log(111);
    resolve("ok");
  }, 1000);
});

p.then(
  (data) => {
    console.log(987879);
    return "1";
  },
  (error) => {
    console.log(error);
    return "2";
  }
);

p.then(
  (data) => {
    console.log(data, 798798);
    return "1";
  },
  (error) => {
    console.log(error);
    return "2";
  }
)
  .then()
  .then(() => {
    console.log(666);
  });

整体架构

由于promise实现比较长,所以,每一次默写,应该是先搭建架子,然后默写注释每个函数做了什么,

一:搭建架子

主体,then,catch

二:主体要做什么

  1. 内部值定义:状态,结果,then的回调函数存储,resolve,reject函数
  2. 执行回调函数,trycatch兜底
resolve,reject函数
  1. 状态变更
  2. 保存结果
  3. 执行回调函数(微任务)

三:then要做什么

  1. 返回promise
  2. promise的结果结果then执行的结果而定:promise,非promise,报错
  3. 状态可能是等待状态和有结果状态,有结果执行执行回调函数,等待保存回调函数
  4. 回调函数可以不传
  5. 微任务环境下执行

四:catch要做什么

为then功能的一部分,就是reject的那部分

构造函数主体做了什么(new的时候)

  1. 传入一个函数fn,这个函数有两个参数,resolve,reject,这两个参数也都是函数。
  2. new的时候,立即执行这个函数,同步执行的。
  3. 定义两个床给fn的函数resolve,reject,这两个函数是promise内部传给使用者的,这两个函数都接受一个参数data,这个参数是结果。
  4. 定义一个promise的状态status,初始值为pedding,定义promise的结果result,初始值为null。
  5. 传入的函数fn抛异常的时候,能捕捉到,状态会变成reject,所以要用try catch。

resolve和reject函数做了设么

  1. 修改状态为成功/失败
  2. 设置当前promise执行完的结果值result,以供后面使用
  3. 执行存储的回调函数
  4. 状态只能修改一次,所以修改状态之前要先判断
  5. 微任务异步执行

then方法做了什么

  1. 是原型方法

  2. 接收两个参数,都是函数,前一个是成功时候的回调,后一个是失败时候的回调

  3. 如果状态是成功,立即执行成功回调

  4. 如果状态是失败,立即执行失败回调

  5. 如果状态是等待(异步情况),把成功回调函数和失败函数存起来,等状态改变后,由resolve或者reject函数执行。并且由于promise成功后,是可以执行多个then的,所以应该把成功回调函数存成数组。

  6. then方法会返回一个新的promise,promise的结果由传入的回调函数的结果而定。分别有四种情况:

    a. 没有return结果,是understand,那么则把promise结果设置为成功,值为undefined。

    b. 有return结果,return结果只要不是promise,则同样把promise设置为成功,接值为该return的结果。

    c. 有return结果,结果为一个新的promise,则以这个新的promise的结果为结果,新的promise是成功的,则返回的promise是成功,新的promise结果是失败的,则返回的promise失败。

    d. 如果回调函数在执行过程中报错,则返回失败的promise,所以应该用try catch包裹一下。

    e. 注意:即便我们最初的promise的执行结果是失败的,但是被reject函数兜住以后,结果仍为成功,除非新的回调函数内返回的的promise为失败才可能是再次return失败。

  7. 异步的情况,then不能直接得到promise的结果,需要等待异步的结果,再执行新的promise的resolve函数或者reject函数,为了达到次目标,存起来的函数不应该直接为onResolved/onRejected这两个回调函数,应该在外包装一层函数,这一层函数内执行onResolved/onRejected函数,同时根据结果执行新的返回的promi的resolve/reject函数。

  8. then方法允许用户不传参数,且依然要有返回一个promise,由于then是链式调用,所以then方法即便没有传入参数,也要默认给一个成功回调和一个失败回调,这个回调的参数是promise的结果。

  9. promise的then方法异步微任务执行的,优先用queueMicrotask函数模拟,次选setTimeout(宏任务)

then方法里的两个函数参数onResolved,onRejected做了什么

  1. 这两个函数可能同步执行(当promise状态被同步修改掉,没有写异步代码时),也可能异步执行。
  2. 这两个函数传入的参数是promise的结果,所以这就是为什么之前结果要保存下来。

catch方法做了什么

  1. catch也是返回一个promise,但是一定是失败的。
  2. catch的功能是then功能的一部分,所以调用then方法就行。

Promise.resolve方法做了什么

  1. 是静态方法

  2. 也是返回一个Promise,这个Promise的结果:

    a. 如果Promise.resolve的参数是一个promise对象,则根据这个promise的对象的结果来定

    b. 如果参数不是promise对象,则无论返回什么,都是成功

    c. 如果参数执行过程中报错,则返回失败

    d. 返回的promise的结果的参数,就是调用的时候传入的参数,如果传入的是函数,则是函数内部return的值。

Promise.resolve方法做了什么

  1. 是静态方法
  2. 也是返回一个Promise,这个Promise的结果,永远是reject,不论传入了什么。

Promise.all方法做了什么

  1. 是静态方法
  2. 返回的结果也是promise对象
  3. 如果所有promise都成功,则返回成功,成功的参数是一个数组,里面按照执行顺序,保存了每一个promise的成功结果。
  4. 如果失败,则返回那个失败的结果。

Promise.race方法做了什么

  1. 是静态方法
  2. 返回的结果也是promise对象
  3. 只返回最快得到结果的那个promise,那个promise是成功就返回成功,否则返回失败

完整版promise

function MyPromise(fn) {
  this.PEDDING = "pedding";
  this.FULFILLED = "fulfilled";
  this.REJECTED = "rejected";
  this.result = null;
  this.status = "pedding";
  this.callbacks = [];

  const resolve = (data) => {
    if (this.status !== this.PEDDING) return;
    this.status = this.FULFILLED;
    this.result = data;
    queueMicrotask(() => {
      this.callbacks.forEach((item) => {
        item.onResolved(this.result);
      });
    });
  };
  const reject = (data) => {
    if (this.status !== this.PEDDING) return;
    this.status = this.REJECTED;
    this.result = data;

    queueMicrotask(() => {
      this.callbacks.forEach((item) => {
        item.onRejected(this.result);
      });
    });
  };

  // 立即执行
  try {
    fn(resolve, reject);
  } catch (error) {
    reject(error);
  }
}

MyPromise.prototype.then = function (onResolved, onRejected) {
  const self = this;
  if (typeof onResolved !== "function") {
    onResolved = (data) => {
      return data;
    };
  }
  if (typeof onRejected !== "function") {
    onRejected = (err) => {
      return err;
    };
  }
  return new MyPromise((resolve, reject) => {
    // 回调
    const callback = (type) => {
      try {
        const result = type(self.result);
        if (result instanceof MyPromise) {
          result.then(
            (v) => {
              resolve(v);
            },
            (e) => {
              reject(e);
            }
          );
        } else {
          resolve(self.result);
        }
      } catch (error) {
        reject(error);
      }
    };

    // 回调函数,同步执行的情况
    if (this.status === this.FULFILLED) {
      queueMicrotask(() => {
        callback(this.onResolved);
      });
    }
    if (this.status === this.REJECTED) {
      queueMicrotask(() => {
        callback(this.onRejected);
      });
    }

    // 异步情况
    if (this.status === this.PEDDING) {
      this.callbacks.push({
        onResolved() {
          callback(onResolved);
        },
        onRejected() {
          callback(onRejected);
        },
      });
    }
  });
};




MyPromise.prototype.catch = function(errFn) {
   return this.then(undefined, errFn)
}

MyPromise.resolve = function (payload) {
  return new MyPromise((resolve, reject) => {
    if (payload instanceof MyPromise) {
      payload.then(
        (data) => {
          resolve(data);
        },
        (err) => {
          reject(err);
        }
      );
    } else {
      resolve(payload);
    }
  });
};

MyPromise.reject = function (payload) {
  return new MyPromise((resolve, reject) => {
    reject(payload);
  });
};

MyPromise.all = function (promises) {
  return new MyPromise((resolve, reject) => {
    let count = 0;
    const results = [];
    for (let i = 0; i < promises.length; i++) {
      const element = promises[i];
      element.then(
        (data) => {
          count++;
          results[i] = data;
          if (count === promises.length) {
            resolve(results);
          }
        },
        (err) => {
          reject(err);
        }
      );
    }
  });
};

MyPromise.race = function (promises) {
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      const element = promises[i];
      element.then(
        (data) => {
          resolve(data);
        },
        (err) => {
          reject(err);
        }
      );
    }
  });
};