返回

从零开始 - 40行代码实现一个简单 Promise 函数

前端

引言:异步编程的必要性

在当今快节奏的网络世界中,异步编程已成为一项不可或缺的技术。它允许我们在不阻塞主线程的情况下执行耗时的任务,从而提供流畅且响应迅速的用户体验。然而,理解和实现异步编程却可能让人望而生畏。

认识 Promise

Promise 是 JavaScript 中用于异步编程的强大工具。它本质上是一个表示未来值的占位符,该值可以在将来某个时刻变为可用。

从头开始构建一个 Promise 函数

为了加深理解,让我们从头开始编写一个简单的 Promise 函数。只需 40 行代码,我们就能掌握 Promise 的基本原理:

const Promise = function(executor) {
  // 当前 Promise 的状态
  this.state = 'pending';
  // 保存最终的值或错误
  this.value = undefined;
  // 存储所有注册的回调函数
  this.callbacks = [];

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

// 用于将 Promise 状态从 pending 更改为 fulfilled 的函数
const resolve = value => {
  if (this.state !== 'pending') return;
  this.state = 'fulfilled';
  this.value = value;
  executeCallbacks(this.callbacks);
};

// 用于将 Promise 状态从 pending 更改为 rejected 的函数
const reject = reason => {
  if (this.state !== 'pending') return;
  this.state = 'rejected';
  this.value = reason;
  executeCallbacks(this.callbacks);
};

// 依次执行所有已注册的回调函数
const executeCallbacks = callbacks => {
  for (let callback of callbacks) {
    callback(this.value, this.state);
  }
};

// then() 方法:注册回调函数
Promise.prototype.then = function(onFulfilled, onRejected) {
  const callback = {
    onFulfilled,
    onRejected
  };

  // 如果 Promise 尚未完成,则存储回调函数以供以后执行
  if (this.state !== 'pending') {
    setTimeout(() => executeCallbacks([callback]));
  } else {
    this.callbacks.push(callback);
  }

  // 返回一个新的 Promise,以实现链式调用
  return new Promise((resolve, reject) => {
    callback.onFulfilled = value => {
      if (onFulfilled) {
        try {
          const result = onFulfilled(value);
          if (result instanceof Promise) {
            result.then(resolve, reject);
          } else {
            resolve(result);
          }
        } catch (err) {
          reject(err);
        }
      } else {
        resolve(value);
      }
    };

    callback.onRejected = reason => {
      if (onRejected) {
        try {
          const result = onRejected(reason);
          if (result instanceof Promise) {
            result.then(resolve, reject);
          } else {
            reject(result);
          }
        } catch (err) {
          reject(err);
        }
      } else {
        reject(reason);
      }
    };
  });
};

// catch() 方法:处理拒绝状态的快捷方式
Promise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected);
};

如何使用我们的 Promise 函数

现在我们已经有了自己的 Promise 函数,让我们看看如何使用它:

const promise = new Promise((resolve, reject) => {
  // 模拟耗时的异步操作
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('操作成功');
    } else {
      reject(new Error('操作失败'));
    }
  }, 2000);
});

promise
  .then(result => {
    console.log(result); // 输出:"操作成功"
  })
  .catch(error => {
    console.log(error); // 输出:Error('操作失败')
  });

结论

通过动手构建一个简单的 Promise 函数,我们加深了对异步编程的理解。Promise 为管理异步操作提供了强大的方式,让我们能够构建响应迅速且易于维护的应用程序。即使只有 40 行代码,我们也能掌握 Promise 的基本原理,为我们的编码工具箱增添一项宝贵的能力。