[译]JavaScript: Promises 介绍及为何 Async/Await 最终取得胜利

6,194 阅读6分钟

原文地址:JavaScript: Promises and Why Async/Await Wins the Battle

异步函数在JavaScript中有好有坏。好的一面是异步函数是非阻塞的,因此很快 - 特别是在Node.js上下文中。缺点是处理异步函数可能很麻烦,因为有时必须等待一个函数完成才能在进行下一次执行之前获得“回调”。

有一些方法可以发挥异步函数调用的优势并正确处理它们的执行,但其中一种方法远远优于其他方法(Spoiler:它是Async / Await)。在本文中,您将了解使用PromisesAsync/Await的来龙去脉,以及我们对两者之间如何比较的看法。

Promises vs. Callbacks

作为JavaScript或Node.js开发人员,正确理解PromisesCallbacks之间的区别以及它们如何协同工作至关重要。

两者之间存在微小但重要的差异。在每个Promise的核心,都有一个Callback解决某些数据(或错误),这些数据会被调用到Promise

回调处理程序:

调用validatePassword()功能:
下面的代码片段显示了验证密码的完整端到端检查(它是静态的,必须匹配“bambi”):
代码注释得非常好,但是,如果您感到困惑,catch只会在reject()promise调用时执行。由于密码不匹配,我们调用reject(),因此“catch”错误并将其发送到done()函数。

Promises

与传统的基于回调的方法相比,Promise为执行、组合和管理异步操作提供了更简单的替代方案。它们还允许你使用类似同步try / catch的方法处理异步错误。

Promise还提供三种唯一的状态

  1. Pending- promise的结果尚未确定,因为将产生其结果的异步操作尚未完成。
  2. Fulfilled - 异步操作已完成,并且promise有值。
  3. Rejected - 异步操作失败,promise永远不会实现。在被拒绝状态下,promise有一个reason可以指示操作失败的原因。

promisepending状态时,它可以转换为fulfilledrejected的状态。然而,一旦promise得到fulfilledrejected,它将永远不会过渡到任何其他状态,其value或失败原因不会改变。

缺点👎

Promise不做的一件事是解决所谓的“回调地狱”(原文:The one thing promises don’t do is solve what is called “callback hell”, which is really just a series of nested function calls. ),“回调地狱”实际上只是一系列嵌套函数调用。当然,对于一个调用没关系。但是对于多个调用,您的代码将会难以阅读和维护。

在Promises中循环🎡

为了避免使用JavaScript进行深度嵌套回调,假设可以简单地遍历Promises,将结果返回给对象或数组,并在完成后停止。不幸的是,这并不容易; 由于JavaScript的异步特性,如果循环遍历每个Promise,在代码完成时不会调用“done”事件。

处理这种情况的正确方法是使用Promise.all()。这个函数在它被标记为已完成之前等待所有的Fulfillments(或第一次rejection)。

错误处理💣

使用多个嵌套的Promise调用进行错误处理就像蒙着眼睛的驾驶汽车一样。祝你好运找出哪个Promise犯了错误。你最好的选择是完全删除catch()方法并选择加入全局错误处理程序,如下所示:

浏览器

Node.js
注意:以上两个选项是确保捕获错误的两种方法。如果错过了添加catch()方法,它将被代码吞噬。

Async/Await?🤔

Async/Await允许我们编写看起来是同步的异步JavaScript。在本文的前几部分中,您了解了Promises - 它应该简化异步流并避免回调地狱但它没有。

回调地狱?🔥

Callback-hell是一个用于描述以下场景的术语:

注意:举个例子,这是一个API调用,可以从一个数组中获得4个特定用户。

这样的代码这很难看,也占用了大量的空间。Async/Await是JavaScript的最新和最好的东西,它允许我们不仅避免回调地狱,而且确保我们的代码干净并且错误被正确捕获。我发现Async/Await最令人着迷的是它构建在Promises之上(非阻塞等),并且允许代码可读并且就像读取它是同步的一样。这就是关键所在。

注意:以下是一组API调用的示例,用于从一个数组中检索4个用户,超过一半的代码行:

代码这样写比较优雅,对吗?💃

因为Async/Await是建立在Promises之上的,所以你甚至可以在关键字await使用Promise.all()

注意:由于同步特性,Async/await稍微慢一些。连续多次使用它时应该小心,因为await关键字会停止执行后面的所有代码 - 就像在同步代码中一样。

如何开始使用Async/Await?💻

使用Async/Await非常容易理解和使用。实际上,它可以在最新版本的Node.js中本地使用,并且正在迅速融入浏览器。现在,如果你想在客户端使用它,你需要使用Babel。

异步Async

让我们从async关键字开始。它可以放在function之前,如下所示:

等待Await

关键字await使JavaScript等待promise继续并返回其结果。如下所示:

完整的例子

为什么Async/Await更好?😁

现在我们已经了解了PromisesAsync/Await所提供的很多内容,让我们回顾一下为什么Stream认为Async/Await是代码库的最佳选择。

  1. Async/Await允许使用更少的代码行,更少的输入和更少的错误,提供简洁明了的代码库。最终,它使复杂的嵌套代码再次可读。
  2. 使用try/catch处理错误(在一处,而不是在每个调用中)
  3. 错误堆栈是有意义的,而不是从Promises收到的模糊错误,它们很大并且很难找到错误发生的位置。最重要的是,错误指向错误发生的函数。

最后的想法📃

可以说Async/Await是过去几年中添加到JavaScript中的最强大的功能之一。

花了不到一天的时间来理解语法,看看我们的代码库在这方面是多么糟糕。将我们所有基于Promise的代码转换为Async/Await总共花费了大约两天时间,这实际上是一个完全重写 - 这只是为了说明使用Async/Await时需要更少的代码。