Skip to content
Published:

实现一个 basic Promise

实现 Promise 的核心功能:

Table of contents

Open Table of contents

constrauctor

// 1. 定义类
class MyPromise {
  // 2. 添加构造函数
  constructor(func) {
    // 3. 定义 resolve/reject
    const resolve = value => {
      console.log("resolve", value);
    };

    const reject = reason => {
      console.log("reject", reason);
    };

    // 4. 指定回调函数
    func(resolve, reject);
  }
}

state

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
  // 1. 添加状态
  state = PENDING;
  // 2. 添加原因
  result = undefined;
  constructor(func) {
    // 3. 调整 resolve/reject
    // 4. 状态不可逆
    const resolve = value => {
      if (this.state !== PENDING) return;
      // 改状态
      this.state = FULFILLED;
      // 记录原因
      this.result = reason;
      console.log("resolve", value);
    };

    const reject = reason => {
      if (this.state !== PENDING) return;
      // 改状态
      this.state = REJECTED;
      // 记录原因
      this.result = reason;
    };

    func(resolve, reject);
  }
}

实例方法

then

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
  state = PENDING;
  result = undefined;
  constructor(func) {
    const resolve = value => {
      if (this.state !== PENDING) return;
      this.state = FULFILLED;
      this.result = reason;
      console.log("resolve", value);
    };

    const reject = reason => {
      if (this.state !== PENDING) return;
      this.state = REJECTED;
      this.result = reason;
    };

    func(resolve, reject);
  }

  // 1. 添加实例方法
  then(onfulfilled, onrejected) {
    // 2. 参数判断
    onfulfilled = typeof onfulfilled === "function" ? onfulfilled : v => v;
    onrejected =
      typeof onrejected === "function"
        ? onrejected
        : e => {
            throw e;
          };
    // 2.1 执行成功/失败回调
    if (this.state === FULFILLED) {
      onfulfilled(this.result);
    } else if (this.state === REJECTED) {
      onrejected(this.result);
    }
  }
}

异步和多次调用

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
  state = PENDING;
  result = undefined;
  // 1. 定义实例属性
  #handlers = [];
  constructor(func) {
    const resolve = value => {
      if (this.state !== PENDING) return;
      this.state = FULFILLED;
      this.result = value;
      // 3. 调用成功回调
      this.#handlers.forEach(h => {
        h.onfulfilled(this.result);
      });
    };

    const reject = result => {
      if (this.state !== PENDING) return;
      this.state = REJECTED;
      this.result = result;
      // 3. 调用失败回调
      this.#handlers.forEach(h => {
        h.onrejected(this.result);
      });
    };

    func(resolve, reject);
  }

  then(onfulfilled, onrejected) {
    onfulfilled = typeof onfulfilled === "function" ? onfulfilled : v => v;
    onrejected =
      typeof onrejected === "function"
        ? onrejected
        : e => {
            throw e;
          };
    if (this.state === FULFILLED) {
      onfulfilled(this.result);
    } else if (this.state === REJECTED) {
      onrejected(this.result);
    } else if (this.state === PENDING) {
      // 2. 保存回调函数
      this.#handlers.push({
        onfulfilled: () => {
          onfulfilled(this.result);
        },
        onrejected: () => {
          onrejected(this.result);
        },
      });
    }
  }
}

异步任务 - 函数封装

// 1. 定义函数
function runAsyncTask(callback) {
  // 2. 调用核心 api 们
  if (typeof queueMicrotask === "function") {
    queueMicrotask(callback);
  } else if (typeof MutationObserver === "function") {
    const observer = new MutationObserver(callback);
    const textNode = document.createTextNode("");
    observer.observe(textNode, { characterData: true });
    textNode.data = "1";
  } else if (typeof setTimeout === "function") {
    setTimeout(callback, 0);
  } else {
    throw new Error("No async task available");
  }
}
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
  state = PENDING;
  result = undefined;
  #handlers = [];
  constructor(func) {
    const resolve = value => {
      if (this.state !== PENDING) return;
      this.state = FULFILLED;
      this.result = value;
      this.#handlers.forEach(h => {
        h.onfulfilled(this.result);
      });
    };

    const reject = result => {
      if (this.state !== PENDING) return;
      this.state = REJECTED;
      this.result = result;
      this.#handlers.forEach(h => {
        h.onrejected(this.result);
      });
    };

    func(resolve, reject);
  }

  then(onfulfilled, onrejected) {
    onfulfilled = typeof onfulfilled === "function" ? onfulfilled : v => v;
    onrejected =
      typeof onrejected === "function"
        ? onrejected
        : e => {
            throw e;
          };
    // 3. 使用封装函数
    if (this.state === FULFILLED) {
      runAsyncTask(() => {
        onfulfilled(this.result);
      });
    } else if (this.state === REJECTED) {
      runAsyncTask(() => {
        onrejected(this.result);
      });
    } else if (this.state === PENDING) {
      this.#handlers.push({
        onfulfilled: () => {
          runAsyncTask(() => {
            onfulfilled(this.result);
          });
        },
        onrejected: () => {
          runAsyncTask(() => {
            onrejected(this.result);
          });
        },
      });
    }
  }
}

链式编程

fulfilled

/**
 * 链式编程-处理异常和普通内容
 */
class MyPromise {
  // ...

  //* 1. 返回新的 promise 实例
  //* 2. 获取返回值
  //*    2.1 处理返回值
  //*    2.2 处理异常
  //*
  then(onfulfilled, onrejected) {
    onfulfilled = typeof onfulfilled === "function" ? onfulfilled : v => v;
    onrejected =
      typeof onrejected === "function"
        ? onrejected
        : e => {
            throw e;
          };
    // 1. 返回新的 promise 实例
    const p2 = new MyPromise((resolve, reject) => {
      if (this.state === FULFILLED) {
        runAsyncTask(() => {
          // 2. 获取返回值
          try {
            const x = onfulfilled(this.result);
            if (x === p2) {
              throw new TypeError("Chaining cycle detected for promise");
            }
            if (x instanceof MyPromise) {
              x.then(resolve, reject);
            } else {
              resolve(x);
            }
          } catch (e) {
            reject(e);
          }
        });
      } else if (this.state === REJECTED) {
        runAsyncTask(() => {
          onrejected(this.result);
        });
      } else if (this.state === PENDING) {
        this.#handlers.push({
          onfulfilled: () => {
            runAsyncTask(() => {
              onfulfilled(this.result);
            });
          },
          onrejected: () => {
            runAsyncTask(() => {
              onrejected(this.result);
            });
          },
        });
      }
    });
    return p2;
  }
}

reject

// 3. 抽取函数
function resolvePromise(p2, x, resolve, reject) {
  if (x === p2) {
    throw new TypeError("Chaining cycle detected for promise #<Promise>");
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}
class MyPromise {
  // ...
  then(onfulfilled, onrejected) {
    onfulfilled = typeof onfulfilled === "function" ? onfulfilled : v => v;
    onrejected =
      typeof onrejected === "function"
        ? onrejected
        : e => {
            throw e;
          };
    const p2 = new MyPromise((resolve, reject) => {
      if (this.state === FULFILLED) {
        runAsyncTask(() => {
          try {
            const x = onfulfilled(this.result);
            resolvePromise(p2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      } else if (this.state === REJECTED) {
        runAsyncTask(() => {
          // 1. 处理异常
          try {
            // 2. 获取返回值
            const x = onrejected(this.result);
            // 4. 调用函数
            resolvePromise(p2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.state === PENDING) {
        this.#handlers.push({
          onfulfilled: () => {
            runAsyncTask(() => {
              onfulfilled(this.result);
            });
          },
          onrejected: () => {
            runAsyncTask(() => {
              onrejected(this.result);
            });
          },
        });
      }
    });
    return p2;
  }
}

pending

function resolvePromise(p2, x, resolve, reject) {
  if (x === p2) {
    throw new TypeError("Chaining cycle detected for promise #<Promise>");
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}
class MyPromise {
  // ...
  then(onfulfilled, onrejected) {
    onfulfilled = typeof onfulfilled === "function" ? onfulfilled : v => v;
    onrejected =
      typeof onrejected === "function"
        ? onrejected
        : e => {
            throw e;
          };
    const p2 = new MyPromise((resolve, reject) => {
      if (this.state === FULFILLED) {
        if (this.state === FULFILLED) {
          runAsyncTask(() => {
            try {
              const x = onfulfilled(this.result);
              resolvePromise(p2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        } else if (this.state === REJECTED) {
          runAsyncTask(() => {
            try {
              const x = onrejected(this.result);
              resolvePromise(p2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        } else if (this.state === PENDING) {
          this.#handlers.push({
            onfulfilled: () => {
              runAsyncTask(() => {
                // 1. 处理异常
                try {
                  // 2. 获取返回值
                  const x = onfulfilled(this.result);
                  // 调用函数
                  resolvePromise(p2, x, resolve, reject);
                } catch (error) {
                  reject(error);
                }
              });
            },
            onrejected: () => {
              runAsyncTask(() => {
                try {
                  const x = onrejected(this.result);
                  resolvePromise(p2, x, resolve, reject);
                } catch (error) {
                  reject(error);
                }
              });
            },
          });
        }
      }
    });
    return p2;
  }
}

catch

class MyPromise {
  // ...
  constructor(func) {
    // ...
    // 2. 处理异常
    try {
      func(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  catch(onrejected) {
    // 1. 内部调用 then
    return this.then(null, onrejected);
  }
}

finally

class MyPromise {
  // ...
  finally(onfinally) {
    return this.then(onfinally, onfinally);
  }
}

静态方法

resolve

class MyPromise {
  // ...
  static resolve(value) {
    // 1. 判断传入值 Promise 直接返回
    if (value instanceof MyPromise) return value;
    // 2. 转为 Promise 并返回
    return new MyPromise(resolve => {
      resolve(value);
    });
  }
}

reject

class MyPromise {
  // ...

  // 1. 返回 rejected 状态的 Promise
  static reject(value) {
    return new MyPromise((_, reject) => {
      reject(value);
    });
  }
}

race

class MyPromise {
  // ...
  static race(promises) {
    // 1. 返回 Promise
    return new MyPromise((resolve, reject) => {
      // 2. 判断是否为数组
      if (!Array.isArray(promises))
        return reject(new TypeError("promises must be an array"));
      // 等待第一个敲定
      promises.forEach(p => {
        MyPromise.resolve(p).then(resolve, reject);
      });
    });
  }
}

all

class MyPromise {
  // ...
  static all(promises) {
    // 1. 返回 Promise
    return new MyPromise((resolve, reject) => {
      // 2. 判断是否为数组
      if (!Array.isArray(promises))
        return reject(new TypeError("promises must be an array"));
      // 3. 空数组直接兑现
      promises.length === 0 && resolve([]);
      // 4.1 记录结果
      const result = [];
      let count = 0;
      promises.forEach((p, index) => {
        MyPromise.resolve(p).then(
          res => {
            // result.push 无法保证结果的顺序和 promises 数组的顺序一致
            // index 和 promises 的索引一致,保证顺序
            result[index] = res;
            // 4.2 判断全部兑现
            count++;
            if (count === promises.length) resolve(result);
          },
          // 5. 处理第一个拒绝
          reject
        );
      });
    });
  }
}

allSettled

class MyPromise {
  // ...
  static allsettled(promises) {
    // 1. 返回 promise
    return new MyPromise(resolve => {
      // 2. 数组判断
      if (!Array.isArray(promises))
        return resolve(new TypeError("promises must be an array"));
      // 3. 为空直接敲定
      promises.length === 0 && resolve([]);

      // 4. 等待全部敲定
      // 4.1 记录结果
      const result = [];
      let count = 0;
      promises.forEach((p, index) => {
        MyPromise.resolve(p)
          .then(
            res => {
              // 4.2 处理兑现
              result[index] = { status: "fulfilled", value: res };
            },
            err => {
              // 处理拒绝
              result[index] = { status: "rejected", reason: err };
            }
          )
          .finally(() => {
            count++;
            if (count === promises.length) resolve(result);
          });
      });
    });
  }
}

any

class MyPromise {
  // ...
  static any(promises) {
    // 1. 返回 promise, 数组判断
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises))
        return reject(new TypeError("promises must be an array"));
      // 2. 为空直接拒绝
      promises.length === 0 &&
        reject(new AggregateError(promises, "All promises were rejected"));
      // 3. 等待结果
      const errors = [];
      let count = 0;
      promises.forEach((p, index) => {
        MyPromise.resolve(p).then(
          // 3.1 第一个兑现
          resolve,
          err => {
            // 3.2 全部拒绝
            errors[index] = err;
            count++;
            if (count === promises.length)
              reject(new AggregateError(errors, "All promises were rejected"));
          }
        );
      });
    });
  }
}

Promise A+ 标准单元测试

promises-aplus-tests

module.exports = {
  deferred() {
    const res = {};
    res.promise = new MyPromise((resolve, reject) => {
      res.resolve = resolve;
      res.reject = reject;
    });
    return res;
  },
};