返回

破解Then、Catch的链式调用:用最简单的Promise实现

前端

在步入Promise的实现之旅前,我并未深入研究过A+规范。我通过在控制台中反复试验原生Promise,摸索出了它的特性。我也阅读了一些面试文章,发现它们实现的Promise功能并不全面,而且有些复杂。但庆幸的是,我读到了晨曦时梦见兮大神的Promise极简实现,它巧妙地实现了then的链式调用。

在此基础上,我决定动手打造一个更精简、功能更完备的Promise实现。在我看来,Promise是一个非常优雅的构造,它消除了回调函数的嵌套,让异步编程变得更加清晰简洁。

then、catch的链式调用

then和catch是Promise中不可或缺的两个方法。then用于处理Promise的结果,而catch用于捕获和处理异常。它们的链式调用可以让我们优雅地处理多个异步操作,形成一个流水线式的流程。

举个例子,我们可以使用then链式调用来实现一个简单的异步文件读取操作:

const readFilePromise = new Promise((resolve, reject) => {
  fs.readFile('file.txt', 'utf8', (err, data) => {
    if (err) {
      reject(err);
    } else {
      resolve(data);
    }
  });
});

readFilePromise
  .then(data => {
    console.log('File contents:', data);
    return data.toUpperCase();
  })
  .then(uppercaseData => {
    console.log('Uppercase file contents:', uppercaseData);
  })
  .catch(err => {
    console.error('Error reading file:', err);
  });

在这个例子中,第一个then处理文件读取的结果,并将其转换为大写。第二个then进一步处理大写后的结果,并打印它。如果在任何阶段发生错误,catch块将捕获并处理该错误。

实现then和catch

实现then和catch的关键在于巧妙地利用JavaScript的原型和闭包。

首先,我们定义一个Promise构造函数,它接受一个执行器函数作为参数。执行器函数又接受resolve和reject两个回调函数,用于通知Promise结果或错误。

function Promise(executor) {
  this.state = 'pending'; // 初始状态为pending
  this.value = undefined; // 结果值
  this.reason = undefined; // 错误原因
  this.onFulfilledCallbacks = []; // 存储then回调
  this.onRejectedCallbacks = []; // 存储catch回调

  // 执行器函数执行resolve或reject
  try {
    executor(resolve, reject);
  } catch (err) {
    reject(err);
  }
}

接下来,我们定义then方法。then接受两个回调函数作为参数,分别处理结果和错误。如果Promise当前处于pending状态,则将回调函数存储在相应的数组中,等待Promise结果或错误。如果Promise已经完成(即处于fulfilled或rejected状态),则直接执行对应的回调函数。

Promise.prototype.then = function(onFulfilled, onRejected) {
  return new Promise((resolve, reject) => {
    // 将回调函数存储在相应的数组中
    this.onFulfilledCallbacks.push(() => {
      // 调用onFulfilled回调,并用结果调用resolve
      try {
        const result = onFulfilled(this.value);
        resolve(result);
      } catch (err) {
        reject(err);
      }
    });

    this.onRejectedCallbacks.push(() => {
      // 调用onRejected回调,并用错误原因调用reject
      try {
        const reason = onRejected(this.reason);
        resolve(reason);
      } catch (err) {
        reject(err);
      }
    });

    // 如果Promise已经完成,则直接执行回调函数
    if (this.state === 'fulfilled') {
      this.onFulfilledCallbacks[0]();
    } else if (this.state === 'rejected') {
      this.onRejectedCallbacks[0]();
    }
  });
};

catch方法的实现与then类似,但它只接受一个处理错误的回调函数。如果Promise当前处于pending状态,则将回调函数存储在onRejectedCallbacks数组中。如果Promise已经处于rejected状态,则直接执行回调函数。

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

结语

至此,我们已经实现了一个简单但功能完备的Promise。它支持then和catch的链式调用,可以优雅地处理异步操作。虽然它不如原生Promise强大,但对于许多常见的异步编程场景来说,它已经足够了。

希望这个实现能帮助你加深对Promise的理解,并激发你创建自己的异步编程解决方案。