Skip to content
On This Page

JavaScript_Promise

1. MDN

MDNPromise 对象表示异步操作最终的完成(或失败)以及其结果值。

描述

一个 Promise 是一个代理,它代表一个在创建 promise 时不一定已知的值。它允许你将处理程序与异步操作的最终成功值或失败原因关联起来。这使得异步方法可以像同步方法一样返回值:异步方法不会立即返回最终值,而是返回一个 promise,以便在将来的某个时间点提供该值。

一个 Promise 必然处于以下几种状态之一:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled):意味着操作成功完成。
  • 已拒绝(rejected):意味着操作失败。

一个待定的 Promise 最终状态可以是已兑现并返回一个值,或者是已拒绝并返回一个原因(错误)。当其中任意一种情况发生时,通过 Promise 的 then 方法串联的处理程序将被调用。如果绑定相应处理程序时 Promise 已经兑现或拒绝,这处理程序将被立即调用,因此在异步操作完成和绑定处理程序之间不存在竞态条件。

如果一个 Promise 已经被兑现或拒绝,即不再处于待定状态,那么则称之为已敲定(settled)

你还会听到使用已解决(resolved)这个术语来描述 Promise——这意味着该 Promise 已经敲定(settled),或为了匹配另一个 Promise 的最终状态而被“锁定(lock-in)”,进一步解决或拒绝它都没有影响。原始 Promise 提案中的 States and fates 文档包含了更多关于 Promise 术语的细节。在口语中,“已解决”的 Promise 通常等价于“已兑现”的 Promise,但是正如“States and fates”所示,已解决的 Promise 也可以是待定或拒绝的。例如:

javascript
new Promise((resolveOuter) => {
  resolveOuter(
    new Promise((resolveInner) => {
      setTimeout(resolveInner, 1000);
    }),
  );
});

此 Promise 在创建时已经被解决(因为 resolveOuter 是同步调用的),但它是用另一个 Promise 解决的,因此在内部 Promise 兑现的 1 秒之后才会被兑现。在实践中,“解决”过程通常是在幕后完成的,不可观察,只有其兑现或拒绝是可观察的。

2. 对 Promise 的理解

Promise 是 JavaScript 中用于处理异步操作的一种对象。它代表了一个尚未完成但承诺会在未来某个时候完成的操作,并允许你在操作完成前注册回调函数。Promise 对象主要有三种状态:

  • 1)Pending(等待):初始状态,操作未完成,也没有成功或失败。
  • 2)Fulfilled(已实现):操作成功完成,并得到了一个值。
  • 3)Rejected(已拒绝):操作失败,并返回一个原因。

3. Promise 解决了什么问题

Promise 解决了回调地狱和异步编程管理的问题。在传统的 JavaScript 异步编程中,我们通常会使用回调函数来处理异步操作。随着异步操作逐渐增多,回调函数会嵌套的越来越深,这种现象被称为回调地狱。代码结构变得复杂且难以维护。Promise 为异步编程提供了一种更加优雅的解决方案,使用链式调用来处理异步操作,从而提高了代码的可读性和可维护性。

3.1 回调地狱(Callback Hell)

回调地狱是指回调函数相互嵌套,导致代码难以阅读和维护。举个例子:

javascript
fs.readFile('file1.txt', function(err, data1) {
  if (err) throw err;
  fs.readFile('file2.txt', function(err, data2) {
    if (err) throw err;
    fs.readFile('file3.txt', function(err, data3) {
      if (err) throw err;
      // 继续嵌套下去...
    });
  });
});

如上所示,每个嵌套的回调函数都需要等待前一个操结束后再开始,下一个回调函数嵌套得越来越深,这使得代码变得不直观。

3.2 Promise 的基本用法

Promise 是一个构造函数,通过 new Promise 可以创建一个 Promise 实例。示例如下:

javascript
let myPromise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve("成功!");
  }, 1000);
});

myPromise.then(function(value) {
  console.log(value); // 成功!
}).catch(function(error) {
  console.log(error);
});
javascript
let promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    let success = true; // 这里可以是任何异步操作结果
    if (success) {
      resolve('操作成功'); // 操作成功,传递结果
    } else {
      reject('操作失败'); // 操作失败,传递原因
    }
  }, 1000);
});

promise.then(result => {
  console.log(result); // 操作成功时处理
}).catch(error => {
  console.error(error); // 操作失败时处理
});

3.3 链式调用

Promise 的链式调用可以让我们避免回调地狱。每个 .then 方法都会返回一个新的 Promise,因此可以使用链式调用处理多个异步操作。示例如下:

javascript
fetch(url1)
  .then(response => response.json())
  .then(data => fetch(url2))
  .then(response => response.json())
  .then(data => fetch(url3))
  .then(response => response.json())
  .then(data => {
    console.log("All requests completed!");
  })
  .catch(error => {
    console.error("Error:", error);
  });
javascript
new Promise((resolve, reject) => {
  setTimeout(() => resolve(1), 1000); // 初始 Promise
}).then(result => {
  console.log(result); // 输出 1
  return result * 2; // 返回一个新的值
}).then(result => {
  console.log(result); // 输出 2
  return result * 2;
}).then(result => {
  console.log(result); // 输出 4
}).catch(error => {
  console.error(error);
});

相比回调地狱,Promise 使得代码更加扁平和可读。

4. 静态方法

4.1 Promise.all

Promise 提供了一些辅助方法,比如 Promise.allPromise.race,可以并行处理多个异步操作。

Promise.all:当传入的所有 Promise 都完成(或其中一个失败)时,它会返回一个新的 Promise,新的 Promise 的值是所有 Promise 返回值组成的数组。

javascript
Promise.all([promise1, promise2, promise3]).then(values => {
  console.log(values); // [结果1, 结果2, 结果3]
}).catch(error => {
  console.error("Error:", error);
});

4.2 Promise.race

Promise.race:只要其中一个 Promise 解决或拒绝,它就会返回。

javascript
Promise.race([promise1, promise2, promise3]).then(value => {
  console.log(value); // 最先解决或拒绝的结果
}).catch(error => {
  console.error("Error:", error);
});

4.3 Promise.allsettled

Promise.allSettled:此方法也接收一个 promise 对象数组,并返回一个新的 promise。无论数组中的 promise 是解决还是拒绝,返回的 promise 都会解决,并返回一个包含每个 promise 结果对象的数组,每个结果对象包含两种属性:status("fulfilled" 或 "rejected")和 value 或 reason。

总结来说,Promise.all 更适用于处理所有 promise 都需要成功的情况,而 Promise.allSettled 更适用于需要知道每个 promise 结果的情况。

javascript
// Promise.allSettled 示例
const promise4 = Promise.resolve(3);
const promise5 = new Promise((resolve, reject) => {
  setTimeout(reject, 100, 'error');
});
const promise6 = Promise.resolve(42);

Promise.allSettled([promise4, promise5, promise6]).then(results => {
  results.forEach(result => console.log(result.status));
  // fulfilled
  // rejected
  // fulfilled
});