一窥即将推出的 Promise 新方法

Avatar of Jeremias Menichelli
Jeremias Menichelli

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

Promise 是 JavaScript 引入的最受赞誉的功能之一。在语言中直接内置了原生异步构件,开创了一个新时代,不仅改变了我们编写代码的方式,也为其他优秀的 API(如 fetch!)奠定了基础。

让我们退一步,回顾一下它们最初发布时获得的功能,以及接下来将获得哪些新功能。

不了解 Promise 的概念?我强烈推荐 Jake Archibald 的文章 作为入门指南。

我们今天拥有的功能

让我们快速了解一下目前可以使用 Promise 执行的一些操作。当 JavaScript 引入 Promise 时,它为我们提供了一个 API 来执行异步操作并对它们的成功返回或失败做出反应,一种围绕某些数据或结果创建关联的方式,而我们仍然不知道其值。

以下是我们今天拥有的 Promise 功能。

处理 Promise

每次异步方法返回一个 Promise 时(例如,当我们使用 fetch 时),我们可以使用管道 then() 在 Promise *fulfilled*(已完成)时执行操作,并使用 catch() 对 Promise *rejected*(已拒绝)做出响应。

fetch('//resource.to/some/data')
  .then(result => console.log('we got it', result.json()))
  .catch(error => console.error('something went wrong', error))

经典用例是从 API 调用数据,并在数据返回时加载数据,或者如果找不到数据则显示错误消息。

此外,在其初始版本中,我们获得了两种处理 Promise 组的方法。

解析和拒绝 Promise 集合

当 Promise 成功解析时,它可以被 *fulfilled*(已完成),当它以错误解析时,它可以被 *rejected*(已拒绝),并且在没有解析时,它处于 *pending*(挂起)状态。当 Promise 已解析时,无论结果如何,它都被认为是 settled(已完成)。

因此,我们有两种方法可以帮助处理 Promise 组的行为,具体取决于我们获得的状态组合。

Promise.all 就是其中一种方法。只有当所有 Promise 都成功解析时,它才会 fulfilled(已完成),并返回一个包含每个 Promise 结果的数组。如果其中一个 Promise 失败,Promise.all 将进入 catch 并返回错误原因。

Promise.all([
    fetch('//resource.to/some/data'),
    fetch('//resource.to/more/data')
  ])
  .then(results => console.log('We got an array of results', results)
  .catch(error => console.error('One of the promises failed', error)

在这种情况下,Promise.all 将在集合的成员之一抛出错误时短路并进入 catch,或者在所有 Promise 都 *fulfilled*(已完成)时 settled(已完成)。

查看 Domenic Denicola 关于 Promise 状态的这篇 简短文章,以更详细地了解其措辞和概念。

我们还有 Promise.race,它会立即解析它获得的第一个 Promise,无论它是 fulfilled(已完成)还是 rejected(已拒绝)。第一个 Promise 解析后,其余 Promise 将被忽略。

Promise.race([
    fetch('//resource.to/some/data'),
    fetch('//resource.to/other/data')
  ])
  .then(result => console.log('The first promise was resolved', result))
  .catch(reason => console.error('One of the promises failed because', reason))

新成员

好的,我们将把注意力转向可以期待的新的 Promise 功能。

Promise.allSettled

下一个提议引入的成员是 Promise.allSettled,顾名思义,它只在数组中所有集合成员不再处于 pending(挂起)状态时继续执行,无论它们是 *rejected*(已拒绝)还是 *fulfilled*(已完成)。

Promise.allSettled([
    fetch('//resource.to/some/data'),
    fetch('//resource.to/more/data'),
    fetch('//resource.to/even/more/data')
  ])
  .then(results => {
    const fulfilled = results.filter(r => r.status === 'fulfilled')
    const rejected = results.filter(r => r.status === 'rejected')
  })

请注意,这与 Promise.all 不同,因为我们永远不会进入 catch 语句。如果我们正在等待将进入 Web 应用程序不同部分的数据集,但希望为每个结果提供更具体的讯息或执行不同的操作,这将非常有用。

Promise.any

下一个新方法是 Promise.any,它允许我们对集合中任何 fulfilled(已完成)的 Promise做出反应,但只有在它们都失败时才会短路。

Promise.any([
    fetch('//resource.to/some/data'),
    fetch('//resource.to/more/data'),
    fetch('//resource.to/even/more/data')
  ])
  .then(result => console.log('a batch of data has arrived', result))
  .catch(() => console.error('all promises failed'))

这有点像 Promise.race,但 Promise.race 会在第一次解析时短路。因此,如果集合中的第一个 Promise 以错误解析,Promise.race 将继续执行。Promise.any 将继续等待数组中其余项目解析,然后再继续执行。

演示

其中一些使用视觉效果更容易理解,因此我创建了一个 小型游乐场,展示了新方法和现有方法之间的区别。

总结

尽管它们仍处于提案阶段,但社区脚本可以模拟本文中介绍的新方法。像 Bluebird 的 anyreflect 就是在等待浏览器支持改进期间的良好 polyfill。

它们还展示了社区如何已经使用这种异步模式,但将它们内置将为 Web 应用程序中的数据获取和异步解析开辟新的可能性。

除了 thencatch 之外,您还可以将 finally 管道化到 Promise 中,Sarah Drasner 撰写了 一篇详细的文章,您可以查看。

如果您想了解更多关于即将推出的 Promise 组合器的信息,V8 博客刚刚发布了一个简短的解释,其中包含指向官方规范和提案的链接。