【javaScript】Promise 设计与实现

Promise 状态设置

Promise 一共由三种状态,pendding, fulfilled rejected。

const PROMISE_STATUS_PENDDING = "pendding";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

Promise 构造函数实现

我们知道 new Promise 时需要传入一个回调函数 executor, 这个函数会被立即执行,并且这个函数会有两个回调函数,resolve, reject。

  • resolve 会将 Promise 状态变成 fulfilled 状态
  • reject 会将 Promise 状态变成 rejected 状态

基本架构

const PROMISE_STATUS_PENDDING = "pendding";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

class ACPromise {
  constructor(executor) {
    // ACPromise 初始状态
    this.status = PROMISE_STATUS_PENDDING;

    const resolve = (res) => {};

    const reject = (reason) => {};

    executor(resolve, reject);
  }
}

resolve 和 reject 实现

我们知道,resolve 时会调用 then 传入的第一个回调函数,reject 时会调用 then 传入的第二个回调函数。并且我们知道 then 传入的函数,是在主任务执行完成后,才执行,所以,回调函数的过程应该在微任务队列种。同时,我们需要 保存 resolve 和 reject 传入的参数,用户 promise 被多次调用then时,传入该值。

const PROMISE_STATUS_PENDDING = "pendding";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

class ACPromise {
  constructor(executor) {
    // ACPromise 初始状态
    this.status = PROMISE_STATUS_PENDDING;
    this.value = void 0;
    this.reason = void 0;
    this.onFulfilledFns = [];
    this.onRejectedFns = [];

    const resolve = (res) => {
      // 保存 res
      this.value = res;
      // 将当前任务加入到微任务队列种
      queueMicrotask(() => {
        // 判断当前状态是否是Pendding状态
        if (this.status !== PROMISE_STATUS_PENDDING) return;
        // 修改Promise 状态
        this.status = PROMISE_STATUS_FULFILLED;

        // 执行then 方法传入的第一个回调函数
        this.onFulfilledFns.forEach((fn) => fn(this.value));
      });
    };

    const reject = (reason) => {
      this.reason = reason;

      // 加入到微任务队列
      queueMicrotask(() => {
        if (this.status !== PROMISE_STATUS_PENDDING) return;

        // 修改 PROMISE 状态
        this.status = PROMISE_STATUS_REJECTED;

        // 执行 then 方法传入的第二个参数
        this.onRejectedFns.forEach((fn) => fn(this.reason));
      });
    };

    executor(resolve, reject);
  }

  
}

Promise then 方法的实现

我们知道then 方法可以传入两个回调函数

  • 第一个回调函数当promise状态变成 fulfilled 时,会被回调。
  • 第二个回调函数当promise状态变成 rejected 时,会被回调。

并且我们知道 then 方法调用会返回一个非pendding 状态的Promise, 它的状态是由传入回调函数执行的返回值决定的,当回调函数抛出错误时调用reject,否则都是resolve.

  then(onFulfilled, onRejected) {
    const defaultOnFulfilled = (res) => res;
    const defaultOnRejected = (err) => {
      throw err;
    };

    // 用于防止链式调用时断层
    // 就比如说当没有传递第二个参数时,此时抛出错误并不会返回一个Promise,这就会照成后面的捕获错误回调函数,捕获不到
    if (!onFulfilled) onFulfilled = defaultOnFulfilled;
    if (!onRejected) onRejected = defaultOnRejected;

    return new ACPromise((resolve, reject) => {
      // 判断当前Promise状态
      if (this.status === PROMISE_STATUS_FULFILLED) {
        try {
          const result = onFulfilled(this.value);
          resolve(result);
        } catch (e) {
          reject(e);
        }
      }

      if (this.status === PROMISE_STATUS_REJECTED) {
        try {
          const result = onRejected(this.reason);
          resolve(result);
        } catch (e) {
          reject(e);
        }
      }

      if (this.status === PROMISE_STATUS_PENDDING) {
        // 如果还是Pendding状态,表示当前主任务还在执行,将回调函数加入到队列种
        this.onFulfilledFns.push((value) => {
          try {
            const result = onFulfilled(value);
            resolve(result);
          } catch (e) {
            reject(e);
          }
        });

        this.onRejectedFns.push((reason) => {
          try {
            const result = onRejected(reason);
            resolve(result);
          } catch (e) {
            reject(e);
          }
        });
      }
    });
  }

测试then 方法:

// 测试
const promise = new ACPromise((resolve, reject) => {
  // resolve("成功哈哈哈哈");
  reject("失败哈哈哈哈");
});

promise
  .then(
    (res) => {
      console.log("res: ", res);
      return "我是then方法第一个函数的返回值";
    },
    (err) => {
      console.log("err: ", err);
      return "我是then方法第二个函数的返回值";
    }
  )
  .then(
    (res) => {
      console.log(res);
    },
    (err) => {
      console.log(err);
    }
  );

err:  失败哈哈哈哈
我是then方法第二个函数的返回值

Promise 静态方法 resolve 实现

我们知道原生的Promise 静态方法 resolve 它接受一个参数,返回一个 fulfilled 状态的Promise

  // 返回一个fulfilled状态的Promise,并且包含传入的值
  static resolve(res) {
    return new ACPromise((resolve, reject) => {
      resolve(res);
    });
  }

测试 :

// 测试
const promise = ACPromise.resolve("测试静态方法resolve");
promise.then((res) => {
  console.log(res);  // 测试静态方法resolve
});

Promise 静态方法 reject 实现

我们知道 静态方法 reject也接受一个参数, 并返回也给 状态为 rejected 的 promise

  // 返回一个rejected状态的Promise,并包含传入的值
  static reject(reason) {
    return new ACPromise((resolve, reject) => {
      reject(reason);
    });
  }

测试:

// 测试
const promise = ACPromise.reject("测试静态方法reject");
promise.then(undefined, (err) => {
  console.log(err); // 测试静态方法reject
});

Promise 静态方法 all 实现

我们知道 all 接受一组 promise, 并且等待所有 promise,状态全部变成 fulfilled 时,返回一个 状态为 fulfilled 的Promise, 并且附带这一组的结果。如果其中一个状态变成了 rejected ,那么就返回一个 状态为 rejected 的Promise, 并包含第一个 rejected 的错误信息

实现代码:

// all 方法实现
static all(promises) {
    const result = [];
    return new ACPromise((resolve, reject) => {
        promises.forEach((promise) => {
            promise.then(
                (res) => {
                    result.push(res);
                    // 所有的promise都变成了 fulfilled 状态
                    if (result.length === promises.length) resolve(result);
                },
                (err) => {
                    // 一个变成 rejected 状态立马修改新的Promise状态为 rejected
                    reject(err);
                }
            );
        });
    });
}

测试所有都是 fulfilled

// 测试
const p1 = new ACPromise((resolve, reject) => {
  resolve("Promise1");
});
const p2 = new ACPromise((resolve, reject) => {
  resolve("Promise2");
});


const promise = ACPromise.all([p1, p2]);
promise.then((res) => {
  console.log("全部变成fulfilled返回结果为: ", res);
});
// 输出: 全部变成fulfilled返回结果为:  [ 'Promise1', 'Promise2' ]

测试存在一个rejected

// 测试
const p1 = new ACPromise((resolve, reject) => {
  resolve("Promise1");
});
const p2 = new ACPromise((resolve, reject) => {
  // resolve("Promise2");
  reject("reject2");
});

const promise = ACPromise.all([p1, p2]);
promise.then(
  (res) => {
    console.log("全部变成fulfilled返回结果为: ", res);
  },
  (err) => {
    console.log(err);
  }
);
// 输出 reject2

Promise 静态方法 allSettled实现

我们知道 allSettled接受 一组 Promise, 并且等待所有Promise状态都变成 非Pendding状态后,才返回一个 fulfilled 状态的Promise, 并且该方法始终返回 fulfilled 状态的Promise。

实现代码:

static allSettled(promises) {
  const result = [];
  return new ACPromise((resolve, reject) => {
    promises.forEach((promise) => {
      promise.then(
        (res) => {
          result.push({ status: PROMISE_STATUS_PENDDING, value: res });
          if (result.length === promises.length) resolve(result);
        },
        (err) => {
          result.push({ status: PROMISE_STATUS_REJECTED, reason: err });
          if (result.length === promises.length) resolve(result);
        }
      );
    });
  });
}

测试

// 测试
const p1 = new ACPromise((resolve, reject) => {
  resolve("fulfilled1");
});
const p2 = new ACPromise((resolve, reject) => {
  // resolve("Promise2");
  reject("rejected2");
});

const promise = ACPromise.allSettled([p1, p2]);
promise.then((res) => {
  console.log(res);
});
/*
	输出
	[
      { status: 'pendding', value: 'fulfilled1' },
      { status: 'rejected', reason: 'rejected2' }
    ]
*/

Promise 静态方法 race 实现

我们 知道 静态方法 race 接受一组Promise,并 会让第一个状态改变的Promise来决定返回Promise 的状态和回调函数的值。

static race(promises) {
    return new ACPromise((resolve, reject) => {
      promises.forEach((promise) => {
        promise.then(
          (res) => {
            resolve(res);
          },
          (err) => {
            reject(err);
          }
        );
      });
    });
  }

测试:

// 测试
const p1 = new ACPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("fulfilled1");
  }, 1000);
});
const p2 = new ACPromise((resolve, reject) => {
  // resolve("Promise2");
  setTimeout(() => {
    reject("rejected2");
  }, 500);
});

const promise = ACPromise.race([p1, p2]);
promise.then(
  (res) => {
    console.log("res: ", res);
  },
  (err) => {
    console.log("err: ", err);
  }
);

Promise 静态方法 any 实现

静态方法 any 接受一组 Promise,并且只要这一组种 存在一个 fulfilled 状态的Promise就返回,fulfilled 状态的Promise, 否则直接返回一个 new AggregateError(reasons)。

static any(promises) {
    const reasons = [];
    return new ACPromise((resolve, reject) => {
        promises.forEach((promise) => {
            promise.then(
                (res) => resolve(res),
                (err) => {
                    reasons.push(err);
                    // 如果全部都是rejected则返回一个错误对象
                    if (reasons.length === promises.length)
                        reject(new AggregateError(reasons));
                }
            );
        });
    });
}

测试

const p1 = new ACPromise((resolve, reject) => {
  setTimeout(() => {
    reject("ssss");
  }, 2000);
});

const p2 = new ACPromise((resolve, reject) => {
  setTimeout(() => {
    reject("reject");
  }, 100);
  // reject("ssss");
});

ACPromise.any([p1, p2]).then(
  (res) => {
    console.log(res);
  },
  (err) => {
    console.log(err.errors);
  }
);
// [ 'reject', 'ssss' ]

完整代码

const PROMISE_STATUS = {
  PENDING: "pending",
  FULFILLED: "fulfilled",
  REJECTED: "rejected"
};


const tryCatchFuncResolveReject = (data, func, resolve, reject) => {
  try {
    const res = func(data);
    resolve(res);
  } catch(e) {
    reject(e);
  }
};

class WkPromise {
  constructor(executor) {
    this.status = PROMISE_STATUS.PENDING;
    this.onFulfilledFuncs = [];
    this.onRejectedFuncs = [];
    this.value = void 0;
    this.reason = void 0;
    const resolve = (value) => {
      this.value = value;
      
      if (this.status !== PROMISE_STATUS.PENDING)
        return;
      this.status = PROMISE_STATUS.FULFILLED;
      
      queueMicrotask(() => {
        this.onFulfilledFuncs.forEach(fn => fn(value));
      });
    };

    const reject = (reason) => {
      this.reason = reason;
      
      if (this.status !== PROMISE_STATUS.PENDING)
        return;
      this.status = PROMISE_STATUS.REJECTED;
      
      queueMicrotask(() => {
        this.onRejectedFuncs.forEach(fn => fn(reason));
      });
    };

    try {
      executor(resolve, reject);
    } catch(e) {
      reject(e);
    }
  }

  then(onFulfilledFunc, onRejectedFunc) {
    const defaultOnfulfilledFunc = (value) => value;
    const defaultOnRejectedFunc = (reason) => { throw reason; };

    onFulfilledFunc = onFulfilledFunc || defaultOnfulfilledFunc;
    onRejectedFunc = onRejectedFunc || defaultOnRejectedFunc;

    return new WkPromise((resolve, reject) => {
      
      if (this.status === PROMISE_STATUS.PENDING) {
        this.onFulfilledFuncs.push((value) => {
          tryCatchFuncResolveReject(value, onFulfilledFunc, resolve, reject);
        });

        this.onRejectedFuncs.push((reason) => {
          tryCatchFuncResolveReject(reason, onRejectedFunc, resolve, reject);
        });

        return;
      }

      // 当前Promise如果状态已经确定
      if (this.status === PROMISE_STATUS.FULFILLED) {
        queueMicrotask(() => {
          tryCatchFuncResolveReject(this.value, onFulfilledFunc, resolve, reject);
        });
      }

      if (this.status === PROMISE_STATUS.REJECTED) {
        queueMicrotask(() => {
          tryCatchFuncResolveReject(this.reason, onRejectedFunc, resolve, reject);
        })
      }

    });
  }

  static resolve(value) {
    return new WkPromise((resolve, reject) => resolve(value));
  }

  static reject(reason) {
    return new WkPromise((resolve, reject) => reject(value));
  }

  static all(promises) {
    const result = [];
    return new WkPromise((resolve, reject) => {
      promises.forEach((promise) => {
        promise.then((res) => {
          result.push(res);
          if (result.length === promises.length)
            resolve(result);
        }, (err) => {
          reject(err);
        });
      })
    });
  }

  static allSettled(promises) {
    const result = [];
    return new WkPromise((resolve, reject) => {
      promises.forEach(promise => {
        promise.then((res) => {
          result.push({
            status: "fulfilled",
            value: res
          });
          if (result.length === promises.length)
            resolve(result);
        }, (err) => {
          result.push({
            status: "rejected",
            reason: err
          });
          if (result.length === promises.length)
            resolve(result);
        });
      });
    })
  }

  static race(promises) {
    return new WkPromise((resolve, reject) => {
      promises.forEach(promise => promise.then((res) => resolve(res), (err) => reject(err)));
    });
  }

  static any(promises) {
    const reasons = [];
    return new WkPromise((resolve, reject) => {
      promises.forEach(promise => {
        promise.then(resolve, (err) => {
          reasons.push(err);
          if (reasons.length === promises.length)
            reject(new Error(reasons));
        })
      })
    })
  }
}