三十的博客

手写 Promise

发布时间
阅读量 加载中...

最近在看 Geeker-Admin naive-ui-pro 这两个前端项目,在封装 Axios 时遇到了一个 Promise 的使用觉得很迷惑。

找了尚硅谷的 Promise 教程视频,视频中有演示如何手写一个 Promise。遂记录下来。

方法介绍

方法 作用 返回结果
then() 处理成功/失败结果 新 Promise
catch() 处理失败情况 新 Promise
resolve() 创建成功 Promise 成功状态的 Promise
reject() 创建失败 Promise 失败状态的 Promise
all() 并行执行,全成功 结果数组或第一个失败原因
race() 竞争执行 最先完成的结果

then 方法实现

同步代码状态下

如在使用 resolve、reject 方法亦或者执行异常时,then 方法中的回调函数会被调用。

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>手写Promise</title>
    <!-- <script src="./index.js"></script> -->
  </head>
  <body>
    <script>
      const promise = new Promise((resolve, reject) => {
        // resolve("success");
        // reject("error");
        throw "err";
      });

      promise.then(
        (res) => console.log(res),
        (reason) => console.warn(reason)
      );
    </script>
  </body>
</html>

实现 then 方法的基础版本,处理同步代码状态下的 Promise。

js
function Promise(exector) {
  this.PromiseState = "pending";
  this.PromiseResult = undefined;

  // 保存实例对象的this
  const self = this;

  function resolve(value) {
    if (self.PromiseState !== "pending") return;
    self.PromiseState = "fulfilled";
    self.PromiseResult = value;
  }

  function reject(reason) {
    if (self.PromiseState !== "pending") return;
    self.PromiseState = "rejected";
    self.PromiseResult = reason;
  }

  try {
    exector(resolve, reject);
  } catch (err) {
    reject(err);
  }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
  if (this.PromiseState === "fulfilled") {
    onFulfilled(this.PromiseResult);
  }
  if (this.PromiseState === "rejected") {
    onRejected(this.PromiseResult);
  }
};

异步代码状态下

当使用异步任务调用 resolve 或 reject 方法时,then 方法中的回调函数就不会被调用。

js
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
  }, 1000);
});

这时就需要将 then 中添加的回调方法保存起来,等到 Promise 状态更改后再执行相应的回调函数。

js
function Promise(exector) {
  this.PromiseState = "pending";
  this.PromiseResult = undefined;
  this.callback = {};

  // 保存实例对象的this
  const self = this;

  function resolve(value) {
    if (self.PromiseState !== "pending") return;
    self.PromiseState = "fulfilled";
    self.PromiseResult = value;
    if (self.callback.onFulfilled) {
      self.callback.onFulfilled(self.PromiseResult);
    }
  }

  function reject(reason) {
    if (self.PromiseState !== "pending") return;
    self.PromiseState = "rejected";
    self.PromiseResult = reason;
    if (self.callback.onRejected) {
      self.callback.onRejected(self.PromiseResult);
    }
  }

  try {
    exector(resolve, reject);
  } catch (err) {
    reject(err);
  }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
  if (this.PromiseState === "fulfilled") {
    onFulfilled(this.PromiseResult);
  }
  if (this.PromiseState === "rejected") {
    onRejected(this.PromiseResult);
  }
  if (this.PromiseState === "pending") {
    this.callback = {
      onFulfilled,
      onRejected,
    };
  }
};

支持多个 then 方法

实现多个 then 方法的调用,每个 then 方法都可以添加成功/失败的回调函数。

js
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
    // reject("error");
  }, 1000);
});

promise.then(
  (res) => console.log(res),
  (reason) => console.warn(reason)
);

promise.then(
  (res) => alert(res),
  (reason) => alert(reason)
);

按照当前的 Promise 实现,只会执行最后一个添加的 then 回调函数。改造添加回调函数的处理逻辑,将回调函数存储在数组中,在状态更改后按顺序执行。

js
function Promise(exector) {
  this.PromiseState = "pending";
  this.PromiseResult = undefined;
  this.callback = [];

  // 保存实例对象的this
  const self = this;

  function resolve(value) {
    if (self.PromiseState !== "pending") return;
    self.PromiseState = "fulfilled";
    self.PromiseResult = value;
    self.callback.forEach((item) => {
      item.onFulfilled(self.PromiseResult);
    });
  }

  function reject(reason) {
    if (self.PromiseState !== "pending") return;
    self.PromiseState = "rejected";
    self.PromiseResult = reason;
    self.callback.forEach((item) => {
      item.onRejected(self.PromiseResult);
    });
  }

  try {
    exector(resolve, reject);
  } catch (err) {
    reject(err);
  }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
  if (this.PromiseState === "fulfilled") {
    onFulfilled(this.PromiseResult);
  }
  if (this.PromiseState === "rejected") {
    onRejected(this.PromiseResult);
  }
  if (this.PromiseState === "pending") {
    this.callback.push({
      onFulfilled,
      onRejected,
    });
  }
};

获取 then 方法的返回

改造 then 方法,使其返回一个 Promise 对象。

同步代码状态下

js
Promise.prototype.then = function (onFulfilled, onRejected) {
  return new Promise((resolve, reject) => {
    if (this.PromiseState === "fulfilled") {
      try {
        const result = onFulfilled(this.PromiseResult);
        if (result instanceof Promise) {
          result.then(
            (v) => resolve(v),
            (r) => reject(r)
          );
        } else {
          resolve(result);
        }
      } catch (err) {
        reject(err);
      }
    }
    if (this.PromiseState === "rejected") {
      onRejected(this.PromiseResult);
    }
    if (this.PromiseState === "pending") {
      this.callback.push({
        onFulfilled,
        onRejected,
      });
    }
  });
};

异步代码状态下

js
Promise.prototype.then = function (onFulfilled, onRejected) {
  const self = this;
  return new Promise((resolve, reject) => {
    if (this.PromiseState === "fulfilled") {
      try {
        const result = onFulfilled(this.PromiseResult);
        if (result instanceof Promise) {
          result.then(
            (v) => resolve(v),
            (r) => reject(r)
          );
        } else {
          resolve(result);
        }
      } catch (err) {
        reject(err);
      }
    }
    if (this.PromiseState === "rejected") {
      onRejected(this.PromiseResult);
    }
    if (this.PromiseState === "pending") {
      this.callback.push({
        onFulfilled: function () {
          try {
            const result = onFulfilled(self.PromiseResult);
            if (result instanceof Promise) {
              result.then(
                (v) => resolve(v),
                (r) => reject(r)
              );
            } else {
              resolve(result);
            }
          } catch (err) {
            reject(err);
          }
        },
        onRejected: function () {
          try {
            const result = onRejected(self.PromiseResult);
            if (result instanceof Promise) {
              result.then(
                (v) => resolve(v),
                (r) => reject(r)
              );
            } else {
              resolve(result);
            }
          } catch (err) {
            reject(err);
          }
        },
      });
    }
  });
};

方法优化

js
Promise.prototype.then = function (onFulfilled, onRejected) {
  const self = this;
  return new Promise((resolve, reject) => {
    function callback(fn) {
      try {
        const result = fn(self.PromiseResult);
        if (result instanceof Promise) {
          result.then(
            (v) => resolve(v),
            (r) => reject(r)
          );
        } else {
          resolve(result);
        }
      } catch (err) {
        reject(err);
      }
    }

    if (this.PromiseState === "fulfilled") {
      callback(onFulfilled);
    }
    if (this.PromiseState === "rejected") {
      callback(onRejected);
    }
    if (this.PromiseState === "pending") {
      this.callback.push({
        onFulfilled: function () {
          callback(onFulfilled);
        },
        onRejected: function () {
          callback(onRejected);
        },
      });
    }
  });
};

catch&值传递&异常穿透

实现效果:

js
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
    // reject("error");
  }, 1000);
});

promise
  .then()
  .then(() => console.log(111))
  .then(() => {
    throw "err";
  })
  .then(() => console.log(333))
  .catch((reason) => console.warn(reason));

console.log(promise);

实现细节:

js
Promise.prototype.then = function (onFulfilled, onRejected) {
  const self = this;
  if (typeof onFulfilled !== "function") {
    onFulfilled = function (value) {
      return value;
    };
  }
  if (typeof onRejected !== "function") {
    onRejected = function (reason) {
      throw reason;
    };
  }

  return new Promise((resolve, reject) => {
    function callback(fn) {
      try {
        const result = fn(self.PromiseResult);
        if (result instanceof Promise) {
          result.then(
            (v) => resolve(v),
            (r) => reject(r)
          );
        } else {
          resolve(result);
        }
      } catch (err) {
        reject(err);
      }
    }

    if (this.PromiseState === "fulfilled") {
      callback(onFulfilled);
    }
    if (this.PromiseState === "rejected") {
      callback(onRejected);
    }
    if (this.PromiseState === "pending") {
      this.callback.push({
        onFulfilled: function () {
          callback(onFulfilled);
        },
        onRejected: function () {
          callback(onRejected);
        },
      });
    }
  });
};

Promise.prototype.catch = function (onRejected) {
  return this.then(undefined, onRejected);
};

resolve & reject 实现

js
Promise.resolve = function (value) {
  return new Promise((resolve, reject) => {
    if (value instanceof Promise) {
      value.then(
        (v) => resolve(v),
        (r) => reject(r)
      );
    } else {
      resolve(value);
    }
  });
};

Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason);
  });
};

all & race 实现

js
Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    let count = 0;
    let result = [];
    promises.forEach((item, index) => {
      item.then(
        (v) => {
          count++;
          result[index] = v;
          if (count === promises.length) {
            resolve(result);
          }
        },
        (r) => reject(r)
      );
    });
  });
};

Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    promises.forEach((item) => {
      item.then(
        (v) => resolve(v),
        (r) => reject(r)
      );
    });
  });
};

then 方法异步执行

在使用 Promise 时,then 方法是异步执行的,如下面示例打印结果为:111 333 222

js
const p1 = new Promise((resolve, reject) => {
  resolve("success");
  console.log(111);
});

p1.then(() => console.log(222));

console.log(333);

改造 Promise 代码,在合适地方添加 setTimeout 模拟异步

js
function Promise(exector) {
  this.PromiseState = "pending";
  this.PromiseResult = undefined;
  this.callback = [];

  // 保存实例对象的this
  const self = this;

  function resolve(value) {
    if (self.PromiseState !== "pending") return;
    self.PromiseState = "fulfilled";
    self.PromiseResult = value;
    setTimeout(() => {
      self.callback.forEach((item) => {
        item.onFulfilled(self.PromiseResult);
      });
    }, 0);
  }

  function reject(reason) {
    if (self.PromiseState !== "pending") return;
    self.PromiseState = "rejected";
    self.PromiseResult = reason;
    setTimeout(() => {
      self.callback.forEach((item) => {
        item.onRejected(self.PromiseResult);
      });
    }, 0);
  }

  try {
    exector(resolve, reject);
  } catch (err) {
    reject(err);
  }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
  const self = this;
  if (typeof onFulfilled !== "function") {
    onFulfilled = function (value) {
      return value;
    };
  }
  if (typeof onRejected !== "function") {
    onRejected = function (reason) {
      throw reason;
    };
  }

  return new Promise((resolve, reject) => {
    function callback(fn) {
      try {
        const result = fn(self.PromiseResult);
        if (result instanceof Promise) {
          result.then(
            (v) => resolve(v),
            (r) => reject(r)
          );
        } else {
          resolve(result);
        }
      } catch (err) {
        reject(err);
      }
    }

    if (this.PromiseState === "fulfilled") {
      setTimeout(() => {
        callback(onFulfilled);
      }, 0);
    }
    if (this.PromiseState === "rejected") {
      setTimeout(() => {
        callback(onRejected);
      }, 0);
    }
    if (this.PromiseState === "pending") {
      this.callback.push({
        onFulfilled: function () {
          callback(onFulfilled);
        },
        onRejected: function () {
          callback(onRejected);
        },
      });
    }
  });
};

整理封装成 Class

js
class Promise {
  constructor(exector) {
    this.PromiseStatus = "pending";
    this.PromiseResult = undefined;
    this.callbacks = [];

    // 保存实例对象 this 的值
    const self = this;

    function resolve(value) {
      // 这里的 this 是 windows
      // console.log(this)
      if (self.PromiseStatus !== "pending") {
        return;
      }
      self.PromiseStatus = "fulfilled";
      self.PromiseResult = value;
      setTimeout(() => {
        self.callbacks.forEach((item) => {
          item.onFulfilled(value);
        });
      }, 0);
    }
    function reject(reason) {
      if (self.PromiseStatus !== "pending") {
        return;
      }
      self.PromiseStatus = "rejected";
      self.PromiseResult = reason;
      setTimeout(() => {
        self.callbacks.forEach((item) => {
          item.onRejected(reason);
        });
      }, 0);
    }

    try {
      exector(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    const self = this;
    if (typeof onFulfilled !== "function") {
      onFulfilled = (value) => value;
    }
    if (typeof onRejected !== "function") {
      onRejected = (reason) => {
        throw reason;
      };
    }
    return new Promise((resolve, reject) => {
      function callback(type) {
        try {
          const result = type(self.PromiseResult);
          if (result instanceof Promise) {
            result.then(
              (v) => resolve(v),
              (r) => reject(r)
            );
          } else {
            resolve(result);
          }
        } catch (error) {
          reject(error);
        }
      }

      if (this.PromiseStatus === "fulfilled") {
        setTimeout(() => {
          callback(onFulfilled);
        }, 0);
      }
      if (this.PromiseStatus === "rejected") {
        setTimeout(() => {
          callback(onRejected);
        }, 0);
      }
      if (this.PromiseStatus === "pending") {
        this.callbacks.push({
          onFulfilled: function () {
            callback(onFulfilled);
          },
          onRejected: function () {
            callback(onRejected);
          },
        });
      }
    });
  }

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

  static resolve(value) {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(
          (v) => resolve(v),
          (r) => reject(r)
        );
      } else {
        resolve(value);
      }
    });
  }

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

  static all(promises) {
    return new Promise((resolve, reject) => {
      const results = [];
      let count = 0;
      promises.forEach((item, index) => {
        Promise.resolve(item).then(
          (v) => {
            results[index] = v;
            count++;
            if (count === promises.length) {
              resolve(results);
            }
          },
          (r) => {
            reject(r);
          }
        );
      });
    });
  }

  static race(promises) {
    return new Promise((resolve, reject) => {
      promises.forEach((item) => {
        item.then(
          (v) => {
            resolve(v);
          },
          (r) => {
            reject(r);
          }
        );
      });
    });
  }
}
#Promise