Promise

7 阅读6分钟

在JavaScript中处理Promise是一种优雅且有效的方式来管理异步操作,它解决了传统回调函数模式下常见的“回调地狱”问题,并使得代码更加清晰易读。以下是关于如何创建、使用和链式调用Promise的具体方法。

创建一个Promise

要创建一个新的Promise对象,可以使用new Promise()构造函数,该构造函数接收一个执行器函数作为参数。这个执行器函数会在Promise被创建时立即同步执行,并接受两个参数:resolvereject,它们都是函数类型

17

。当异步操作成功完成时调用resolve(value)来表示Promise已被兑现(fulfilled),而当发生错误时则调用reject(reason)来表明Promise被拒绝(rejected)。例如:

javascript
深色版本
const myPromise = new Promise((resolve, reject) => {
  // 模拟异步操作
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('Operation succeeded');
    } else {
      reject('Operation failed');
    }
  }, 1000);
});

使用then()处理结果

一旦有了一个Promise对象,就可以通过.then(onFulfilled, onRejected)方法为其添加处理程序。onFulfilled是当Promise兑现时将被调用的回调函数,而onRejected则是当Promise被拒绝时调用的回调函数。这两个回调函数都可以返回值或者抛出异常,这会影响后续的Promise链

1

。下面是一个简单的例子:

javascript
深色版本
myPromise
  .then(
    result => console.log(result), // 成功时输出 "Operation succeeded"
    error => console.error(error)  // 失败时输出 "Operation failed"
  );

链式调用

Promise最强大的特性之一就是支持链式调用,这意味着可以在同一个Promise上连续地调用多个.then()方法。每个.then()都会返回一个新的Promise对象,因此可以在不破坏原有逻辑的情况下继续附加更多的处理步骤。此外,如果任何一个.then()中返回了另一个Promise,则整个链会等待那个新的Promise完成后再继续

2

。比如:

javascript
深色版本
myPromise
  .then(result => `First step: ${result}`)
  .then(modifiedResult => `Second step: ${modifiedResult}`)
  .catch(error => console.error(`Caught an error: ${error}`));

错误处理

为了简化错误处理,通常会在Promise链的末尾添加一个.catch()方法,它可以捕获链中任何地方发生的错误。值得注意的是,只要有一个.catch()存在,它就会拦截所有之前未被捕获的异常,无论这些异常是在哪个.then()中产生的。这样做的好处是可以集中处理所有的错误情况,而不是分散在各个地方

5

。例如:

javascript
深色版本
myPromise
  .then(processData)
  .then(moreProcessing)
  .catch(handleError); // 统一处理错误

除了.then().catch()之外,还有其他几个有用的静态方法可以帮助我们更好地管理和组合多个Promise:

  • Promise.all(iterables) :当所有的输入Promise都已解决或至少有一个被拒绝时才会解决。它返回一个新的Promise,其结果为一个包含所有输入Promise结果的数组。
  • Promise.race(iterables) :当第一个输入Promise解决或被拒绝时立即解决。它返回一个新的Promise,其结果与最先解决的那个相同。
  • Promise.allSettled(iterables) :等待所有给定的Promise都被解决或拒绝后才解决。它返回一个新的Promise,其结果为一个描述每个输入Promise状态的对象数组。
  • Promise.any(iterables) :只要有一个输入Promise被解决就立即解决。它返回一个新的Promise,其结果为第一个被解决的那个。

这些工具提供了更高级别的抽象,允许开发者编写更为复杂的异步工作流,同时保持代码的简洁性和可维护性

6

综上所述,理解并熟练掌握Promise及其相关API对于现代JavaScript编程至关重要。它们不仅改善了异步代码的结构,还增强了应用程序的稳定性和性能

14

。通过上述介绍,您应该已经掌握了如何在JavaScript中有效地使用Promise来处理异步任务。

实现一个符合Promises/A+规范的Promise类是一项很有价值的学习任务,它不仅加深了对JavaScript异步编程的理解,还帮助掌握语言内部的工作机制。下面我们将逐步构建一个简单的Promise实现,并解释每个部分的功能。

定义Promise构造函数

首先,我们需要定义Promise构造函数,它接收一个执行器(executor)函数作为参数。这个执行器函数会在Promise实例化时立即同步执行,并且会传入两个函数resolvereject给执行器。这两个函数用于改变Promise的状态,从pending变为fulfilledrejected

function MyPromise(executor) {
  let self = this;
  self.status = 'pending'; // Promise 的初始状态
  self.value = undefined;  // 成功后的值
  self.reason = undefined; // 失败的原因
  self.onFulfilledCallbacks = []; // 存储成功的回调
  self.onRejectedCallbacks = [];  // 存储失败的回调

  function resolve(value) {
    if (self.status === 'pending') {
      self.status = 'fulfilled';
      self.value = value;
      self.onFulfilledCallbacks.forEach(cb => cb());
    }
  }

  function reject(reason) {
    if (self.status === 'pending') {
      self.status = 'rejected';
      self.reason = reason;
      self.onRejectedCallbacks.forEach(cb => cb());
    }
  }

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

实现then方法

接下来,我们为MyPromise添加.then()方法。此方法允许用户注册成功和失败的回调函数。重要的是,.then()应该返回一个新的Promise,以便支持链式调用。如果提供的回调函数返回了一个新的Promise,则新创建的Promise应该等待该Promise的结果;否则,它应该立即以返回值兑现。

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  const self = this;
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
  onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

  let promise2 = new MyPromise((resolve, reject) => {
    if (self.status === 'fulfilled') {
      setTimeout(() => {
        try {
          let x = onFulfilled(self.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }, 0);
    }

    if (self.status === 'rejected') {
      setTimeout(() => {
        try {
          let x = onRejected(self.reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }, 0);
    }

    if (self.status === 'pending') {
      self.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onFulfilled(self.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      });

      self.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onRejected(self.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      });
    }
  });

  return promise2;
};

辅助函数:处理then方法的返回值

为了正确处理.then()方法中可能返回的Promise对象或其他类型的值,我们需要编写一个辅助函数resolvePromise。这个函数负责根据返回值的不同情况来决定如何更新新创建的Promise的状态。

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
  }

  let called;
  if ((x != null) && (typeof x === 'object' || typeof x === 'function')) {
    try {
      let then = x.then;
      if (typeof then === 'function') {
        then.call(x, y => {
          if (called) return;
          called = true;
          resolvePromise(promise2, y, resolve, reject);
        }, r => {
          if (called) return;
          called = true;
          reject(r);
        });
        return;
      }
    } catch (e) {
      if (called) return;
      called = true;
      return reject(e);
    }
  }

  resolve(x);
}

添加catch方法

最后,为了让我们的Promise实现更加完整,我们可以简单地为它添加一个.catch()方法。实际上,.catch(onRejected)等价于.then(null, onRejected)

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

通过上述步骤,我们就完成了一个基本但功能完整的Promise实现。当然,实际的Promise实现可能会包含更多的细节和优化,比如性能考量、错误处理以及对各种边界条件的支持等。不过,对于学习目的而言,上面的代码已经足够展示Promise的核心概念和技术要点了。如果您想要进一步探索,可以尝试扩展此实现,加入更多特性如all, race, finally等静态方法,或者深入研究现有的开源Promise库,如Bluebird,它们提供了丰富的API和高效的执行效率。