返回

手撸一个Promise:轻松掌握异步编程精髓

前端

揭秘 JavaScript Promise:从头撸起

理解宏任务与微任务

JavaScript 运行在单线程模型中,但是复杂的操作可能会阻塞线程。为了解决这个问题,JavaScript 引入了两种任务执行模式:

  • 同步模式: 代码顺序执行,前一个任务完成后,后一个任务才能执行。
  • 异步模式: 代码并发执行,任务可以同时执行,完成后将结果返回给主线程。

认识 Promise

Promise 是一种用于处理异步操作的 JavaScript API,它允许你指定异步操作完成后执行的任务,从而简化异步编程。

手撸一个 Promise

要深入理解 Promise,我们可以自己动手实现一个。

1. 构造函数

function Promise(executor) {
  this.state = "pending"; // 初始状态
  this.value = undefined; // 结果值
  this.reason = undefined; // 失败原因
  this.onFulfilledCallbacks = []; // 成功回调函数
  this.onRejectedCallbacks = []; // 失败回调函数

  const resolve = (value) => {
    if (this.state !== "pending") return;
    this.state = "fulfilled";
    this.value = value;
    this.onFulfilledCallbacks.forEach((callback) => callback(value));
  };

  const reject = (reason) => {
    if (this.state !== "pending") return;
    this.state = "rejected";
    this.reason = reason;
    this.onRejectedCallbacks.forEach((callback) => callback(reason));
  };

  executor(resolve, reject); // 立即执行
}

2. then 方法

Promise.prototype.then = function (onFulfilled, onRejected) {
  return new Promise((resolve, reject) => {
    if (this.state === "pending") {
      this.onFulfilledCallbacks.push(() => {
        try {
          const result = onFulfilled(this.value);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      });
      this.onRejectedCallbacks.push(() => {
        try {
          const result = onRejected(this.reason);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      });
    } else if (this.state === "fulfilled") {
      setTimeout(() => {
        try {
          const result = onFulfilled(this.value);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      }, 0);
    } else if (this.state === "rejected") {
      setTimeout(() => {
        try {
          const result = onRejected(this.reason);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      }, 0);
    }
  });
};

3. catch 方法

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

4. finally 方法

Promise.prototype.finally = function (onFinally) {
  return this.then(
    (value) => {
      onFinally();
      return value;
    },
    (reason) => {
      onFinally();
      throw reason;
    }
  );
};

5. Promise.all 方法

Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    const results = [];
    let count = 0;
    promises.forEach((promise) => {
      promise.then(
        (value) => {
          results[count] = value;
          count++;
          if (count === promises.length) {
            resolve(results);
          }
        },
        (reason) => {
          reject(reason);
        }
      );
    });
  });
};

6. Promise.race 方法

Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    promises.forEach((promise) => {
      promise.then(
        (value) => {
          resolve(value);
        },
        (reason) => {
          reject(reason);
        }
      );
    });
  });
};

总结

通过手撸一个 Promise,我们深入了解了其内部工作原理。Promise 作为异步编程的利器,提供了更简洁优雅的解决方案。

常见问题解答

  1. 什么是宏任务和微任务?
    宏任务:浏览器主线程上执行的任务,如 setTimeout、setInterval。微任务:在宏任务执行期间执行的任务,如 Promise 的 then 回调。

  2. Promise 的状态有哪些?
    pending:初始状态,等待操作完成。fulfilled:操作成功完成。rejected:操作失败。

  3. 如何使用 Promise.all?
    将多个 Promise 放在数组中,传入 Promise.all(),当所有 Promise 都完成时,返回结果数组。

  4. Promise.race 和 Promise.all 有什么区别?
    Promise.race 返回第一个完成的 Promise,而 Promise.all 返回所有 Promise 完成后的数组。

  5. 为什么需要 finally 方法?
    finally 方法用于在 Promise 完成后执行一些操作,无论 Promise 成功还是失败。