有了async/await,你可以丢掉promise链了

异步函数可能会一直存在,但有些人认为async/await可能会被抛弃。

为什么?

一个常见的误解是async/await和promise是完全不同的东西。

但其实async/await是基于promise的。

不要因为你使用了promise就被promise链给野蛮绑架了。

在本文中,我们将了解async/await如何让开发人员的生活变得更轻松,以及为什么要停止使用promise链。

让我们来看看一个promise链的例子:

getIssue()  .then(issue =\u0026gt; getOwner(issue.ownerId))  .then(owner =\u0026gt; sendEmail(owner.email, 'Some text'))

现在让我们看看async/await的等效代码:

const issue = await getIssue()const owner = await getOwner(issue.ownerId)await sendEmail(owner.email, 'Some text')

它看起来就像简单的语法糖,对吗?

与大多数人一样,我发现自己的代码看起来很简单、干净、易于阅读。但是,在修改代码时,似乎比预期的困难一些。

但这一点也不奇怪,这正是promise链的问题所在。下面让我们看看这是为什么。

易于阅读,易于维护

假设我们需要对之前的代码做出一个很小的修改(例如,我们需要在电子邮件内容中提及问题编号,比如“Some text #issue-number”)。

我们该怎么做?对于async/await版本,改起来很简单:

const issue = await getIssue()const owner = await getOwner(issue.ownerId)await sendEmail(owner.email, `Some text #${issue.number}`) // tiny change here

前两行不受影响,第三行只需要稍微改动一点点。

那么promise链版本呢?

在.then()中,我们可以访问owner,但不能访问issue。看看,promise链从这里开始就变得有点混乱了。我们可以试着这样修改:

getIssue()  .then(issue =\u0026gt; {    return getOwner(issue.ownerId)      .then(owner =\u0026gt; sendEmail(owner.email, `Some text #${issue.number}`))  })

正如你所看到的,一个小的调整就需要修改好几行代码(如getOwner(issue.ownerId))。

代码在不断发生变化

在开发新功能时尤其如此。例如,如果我们需要将异步调用getSettings()返回的结果包含在电子邮件内容中,该怎么办?

它可能看起来像这样:

const settings = await getSettings() // we added thisconst issue = await getIssue()const owner = await getOwner(issue.ownerId)await sendEmail(owner.email,  `Some text #${issue.number}. ${settings.emailFooter}`) // minor change here

如果使用promise链该怎样实现?可能是这样:

Promise.all([getIssue(), getSettings()])  .then(([issue, settings]) =\u0026gt; {    return getOwner(issue.ownerId)      .then(owner =\u0026gt; sendEmail(owner.email,        `Some text #${issue.number}. ${settings.emailFooter}`))  })

但是,对我来说,这些代码显得有点乱。每当我们需要做出修改时,都需要修改很多代码,这实在太恶心了!

因为我不想再嵌套then()调用,我可以并行地调用getIssue()和getSettings(),所以我使用了Promise.all(),然后进行一些解构。确实,这个版本与await版本相比更好,因为它可以并行运行 ,但它仍然难以阅读。

我们是否可以优化await版本,让它可以并行运行而不需要牺牲代码的可读性?让我们来看看:

const settings = getSettings() // we don't await hereconst issue = await getIssue()const owner = await getOwner(issue.ownerId)await sendEmail(owner.email,  `Some text #${issue.number}. ${(await settings).emailFooter}`) // we do it here

我删除了settings右侧的await,并在sendEmail()前面加上了await。我创建了一个promise,但在需要用到这个值之前不需要等待。与此同时,其他代码可以并行运行。就这么简单!

你不需要Promise.all()

我已经演示了如何在不使用Promise.all()的情况下轻松有效地并行运行promise。这意味着你不再需要Promise.all()了,对吧。

有些人可能会争辩说,还有一个情况,也就是当你有一个值数组时,你需要将它映射到一个promise数组。例如,你有一个要读取的文件名的数组,或者你需要下载的URL的数组,等等。

我认为他们错了。我的建议是使用外部库来处理并发。例如,我会使用bluebird中的Promise.map(),因为它支持设置并发限制。如果我要下载N个文件,可以指定同时下载的文件个数不超过M个。

你可以在任何地方使用await

async/await可以帮你简化你要做的事情。想象一下,如果使用promise链,下面这些表达式有多复杂。但是如果使用async/await,它们就会简单得多。

const value = await foo() || await bar()const value = calculateSomething(await foo(), await bar())

还说服不了你?

假设你对代码可阅读性和易维护性不感兴趣,相反,你更喜欢复杂性,那么好吧。

在代码中使用promise链时,开发者每次在调用then()时都会创建新函数。这会占用更多内存,而且这些函数总是处在另一个上下文中。因此,这些函数变成了闭包,这使垃圾回收变得更加困难。此外,这些匿名函数通常会污染堆栈跟踪。

现在,我们讨论的是堆栈跟踪:现在有一个提议用于为异步函数实现更好的堆栈跟踪。

只要开发人员坚持只使用异步函数和异步生成器,并且不会手动编写promise代码,因为如果使用了promise链,就无法实现更好的堆栈跟踪。

这也是总是使用async/await的另一个原因!

如何迁移

首先:开始使用异步函数并停止使用promise链。

其次,你可能已经发现Visual Studio Code可以非常方便地帮你实现迁移。

视频地址:https://twitter.com/umaar/status/1045655069478334464

结论

  • async/await已得到广泛支持,除非你需要支持IE。

  • async/await代码具有更好的可读性和可维护性。

  • 出于一些技术原因,最好是只使用async/await。

  • 借助Visual Studio Code或其他IDE,你可以轻松地迁移现有的promise链代码!

英文原文:https://blog.logrocket.com/promise-chaining-is-dead-long-live-async-await-445897870abc

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值