Promise

状态介绍

Promise 只有三中状态:pendingfulfilledrejected

  • pending:初始状态,表示异步操作尚未完成。这是 Promise 对象刚创建时的状态,表示还没有任何结果。

  • fulfilledfulfilled 是异步操作成功完成,通过调用 resolve(value) 函数触发,其中 value 是操作的结果。 Promise 状态从 pending 变为 fulfilled,并返回一个结果值。

  • rejectedrejected 表示异步操作失败。通过调用 reject(reason) 函数触发,其中 reason 是失败的原因。

如果异步操作成功完成,执行器(executor)将调用 resolve() 函数,将 Promise 的状态从“pending(等待)”改变为“fulfilled(已兑现)”,同时携带一个值。

如果发生错误,执行器将调用 reject() 函数,将 Promise 的状态从“pending(等待)”改变为“rejected(已拒绝)”,并携带错误原因。

一旦 Promise 达到 fulfilled 或 rejected 状态之一,它就会保持在那个状态,并且不能转换到其他状态。

换句话说,一个 Promise 不能从 fulfilled 状态转变为 rejected 状态,反之亦然。同时,也不能从 fulfilled 或 rejected 状态返回到 pending 状态。

一旦创建了一个新的 Promise 对象,其初始状态为 pending。如果这个 Promise 进入了 fulfilled 或 rejected 状态,我们说它是已决议(resolved)的。

Static methods

Promise.all

Promise.all() 可以帮助我们一次,并行处理多个 Promise, 然后将结果汇聚到一个数组中返回。

  • 如果所有 Promise 都解析成功,Promise.all() 会将每个 Promise 已完成的值汇聚到一个数组中返回。我们可以按照原来数组中 Promise 的顺序获取到对应的值。

all-fulfiled
  • 如果任意一个 Promise 解析失败,那么 Promise.all() 会立即返回一个 rejected 的 Promise,并且返回的 Promise 的值为第一个解析失败的 Promise 的值。

  • 如果传入的 Promise 中有一个是 pending 状态,那么 Promise.all() 会等待所有的 Promise 都解析成功或者失败后,才会返回结果。

Promise.allSettled

Promise.allSettled() 可以帮助我们一次,并行处理多个 Promise,并将已改变的状态存放到一个数组中返回。

语法:

返回的数组中的每一项格式如下:

  • promise1 在 t1 时刻被拒绝(reject),并返回一个错误。

  • promise2 在 t2 时刻被兑现(resolve),并返回一个值。

  • Promise.allSettled() 方法会在所有 Promise 都已解决(无论是被拒绝还是被兑现)后,返回一个数组,该数组包含描述 promise1 和 promise2 状态及结果的对象。

示例

Promise.any

Promise.any() 可以帮助我们一次并行处理多个 Promise,并返回第一个解析成功的 Promise 的值。

在此图中:

  • promise1 在 t1 时刻解析为值 v1

  • promise2 在 t2 时刻解析为值 v2。

  • Promise.any() 返回一个 Promise,该 Promise 在 t1 时刻解析为值 v1。即 promise1 的结果。

在此图中:

  • promise1 在 t1 时刻被拒绝,

  • promise2 在 t2 时刻被解析成功,

  • 返回结果为 promise2 的值。所有被拒绝的 Promise 的值会被忽略。

如果迭代对象中的所有 Promise 均被拒绝,或者该迭代对象为空,则 Promise.any() 方法将返回一个被拒绝的 Promise,其包含所有拒绝原因的错误信息。AggregateError 是 Error 的一个子类。

在此图中:

  • promise1 在 t1 时刻被拒绝,

  • promise2 在 t2 时刻被拒绝,

  • promise3 在 t3 时刻被拒绝,

  • 返回结果为 AggregateError 的错误信息。

所有 Promise 都已解析成功

一个 Promise 拒绝

所有 Promise 都拒绝

Promise.race

xx

假设来自服务器的数据加载过程花费的时间超过 5000 毫秒,我们就认为此次请求失败。

Promise.try

在实际编程中,我们经常需要处理一个函数,这个函数可能是同步的,也可能是异步的。为了处理这两种情况,开发者不得不使用如 Promise.resolve().then(fun) 这样的模式,但这会导致即使 fun 是同步函数,它也会在下一个事件循环中异步执行。

Promise.try() 它能把任何函数(同步、异步)包装成一个 Promise。

语法:

参数

  • function:一个同步或异步函数

  • arg1, arg2, ...:可选参数,传递给 function 的参数

返回值

  • 返回一个 Promise 对象

在这个示例中,函数 fun 是同步的,使用 Promise.try 包装 Promise.fun 后,可以在.then 方法中接收到这个值,并且这个处理过程是异步的。但是,与 Promise.resolve().then(fun) 不同的是,使用 Promise.try 时,如果 fun 是同步函数,它会在当前事件循环中立即执行,而不是在下一个事件循环中。

如果 fun 是一个异步函数,那么 Promise.try 同样可以处理这种情况

Promise.withResolvers

在没有 Promise.withResolvers() 方法之前,你可能实现了这样的代码

在 Chrome 119 版本之后,可以使用以下方式实现:

Promise.withResolvers() 方法返回一个包含 promise、resolve 和 reject 的对象。

  • promise 是一个 Promise 对象

  • resolve 是一个函数,用于解析 promise

  • reject 是一个函数,用于拒绝 promise

Promise.withResolvers() 方法的关键区别在于解决和拒绝的函数与 Promise 本身体处于同一作用域,而不是在执行器中创建并且只能使用一次。这使得在某些高级的用例中可能会更加方便,例如在重复事件中重用解决和拒绝函数,特别是在处理流和队列时。这也就意味着嵌套层级较少,而不是在执行器中包含大量逻辑。

在使用 Promise.withResolvers() 的示例中,将 Node.js 的可读流转换为异步可迭代对象,每个 promise 代表一批可用的数据,每次读取当前批次时,将创建一个新的 promise 用于下一批次。请注意,事件监听器只添加了一次,但实际上每次都会调用不同版本的解决和拒绝函数。

此外。Promise.withResolvers() 也 适用于非 Promise 构造函数。可以在任务实现了与 Promise() 构造函数相同签名的构造函数上调用它。 例如,我们可以在一个将 console.log 作为解决和拒绝函数传递给执行器的构造函数上调用它。

错误处理

当一个 Promise 被 reject 时,控制权将移交至最近的 rejection 处理程序。

例如,下面代码中的 fetch 会在 Promise 被 reject 时抛出错误。

我们可以看到 .catch 并不是立即的,它可以在一个或多个 .then 之后出现。

或者,该网址一切正常,但是响应体不是有效的 JSON 数据,捕获 error 的最简单方法是,将 .catch 附加到末尾。

隐式 try...catch

Promise 的执行者(executor)和 Promise 的处理程序周围有一个隐式的 try...catch,如果发生异常,他就会被捕获,并被视为 rejection 进行处理。

例如,下面这段代码:

executor 周围的隐式 try...catch 会自动捕获 error,并将其变为 rejected Promise

如果我们在.then处理程序中 throw,这也意味着 Promise rejected,因此控制权将交至最近的 error 处理程序。

对于所有的 error 都会被捕获,而不仅仅是由 throw 语句导致的这些 error。例如,一个编程中的错误:

最后的 .catch 不仅会补货显示的 rejection,还会捕获上面的处理程序中意为出现的 error

再次抛出

Promise 链后面可以多个 .then.catch 如果在某个 .catch 我们无法处理错误,我们可以选择将其在此抛出。

这里 .catch 块正常完成。所以下一个成功的 .then 处理程序就会被调用。

在下面的例子中,我们可以看到 .catch 的另一种情况。(*) 行的处理程序捕获了 error,但无法处理它(例如,它只知道如何处理 URIError),所以它将其再次抛出:

执行从第一个 .catch (*) 沿着链跳转至下一个 (**)。

未处理的 rejection

当出现 error 时候,Promise 的状态会变为 rejected,然后执行应该跳转至最近的 rejection 处理程序。但是当没有处理 error 时,error 会卡住。

当发生一个常规的 error 并且未被 try..catch 捕获时,JavaScript 脚本死了,并在控制台中留下了一个信息。对于在 Promise 中未被处理的 rejection,也会发生类似的事。

JavaScript 引擎会跟踪此类 rejection,在这种情况下会生成一个全局的 error。如果你运行上面这个代码,你可以在控制台中看到。

在浏览器中,我们可以使用 unhandledrejection 事件来捕获这类 error

果出现了一个 error,并且在这没有 .catch,那么 unhandledrejection 处理程序就会被触发,并获取具有 error 相关信息的 event 对象,所以我们就能做一些后续处理了。

通常此类 error 是无法恢复的,所以我们最好的解决方案是将问题告知用户,并且可以将事件报告给服务器。

在 Node.js 等非浏览器环境中,有其他用于跟踪未处理的 error 的方法。

最后更新于

这有帮助吗?