作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Demir Selmanovic
验证专家 在工程
24 的经验

Demir是一名开发人员和项目经理,在广泛的软件开发角色方面拥有超过15年的专业经验.

分享

编写成功的web应用程序的关键之一是能够在每个页面上进行数十次AJAX调用.

This is a typical 异步hronous programming challenge, 和 how you choose to deal with 异步hronous calls will, 很大程度上, make or break your app, 和 by extension potentially your entire startup.

在很长一段时间里,在JavaScript中同步异步任务是一个严重的问题.

This challenge is affecting back-end 使用节点的开发人员.js 与使用任何JavaScript框架的前端开发人员一样多. 异步编程是我们日常工作的一部分, 但这一挑战往往被轻视,没有在适当的时候加以考虑.

A Brief History of 异步 JavaScript

第一个也是最直接的解决方案是 nested functions as callbacks. This solution led to something called 回调地狱, 和 too m任何 applications still feel 的 burn of it.

然后,我们得到 承诺. This pattern made 的 code a lot easier to read, 但这与“不要重复自己”(DRY)原则相去甚远. 仍然有很多情况下,您必须重复相同的代码片段才能正确地管理应用程序的流. 最新的添加,以异步/等待 JavaScript语句的形式,终于完成了 异步hronous code in JavaScript as easy to read 和 write as 任何 o的r piece of code.

让我们看一下这些解决方案的示例,并回顾一下JavaScript中异步编程的发展历程.

要做到这一点, 我们的异步JavaScript教程将研究一个执行以下步骤的简单任务:

  1. Verify 的 username 和 password of a user.
  2. Get application roles for 的 user.
  3. Log application access time for 的 user.

Approach 1: Callback Hell (“The Pyramid of Doom”)

同步这些调用的古老解决方案是通过嵌套回调. 对于简单的异步JavaScript任务来说,这是一种不错的方法, but wouldn’t scale because of an issue called 回调地狱.

插图:异步JavaScript回调地狱反模式

这三个简单任务的代码看起来像这样:

const verifyUser =函数(用户名,密码,回调){
   数据库.verifyUser(username, password, (error, userInfo) => {
       If (error) {
           回调(错误)
       其他}{
           数据库.将getRoles(username, (error, roles) => {
               如果(错误){
                   回调(错误)
               其他}{
                   数据库.logAccess(username, (error) => {
                       如果(错误){
                           回调(错误);
                       其他}{
                           callback(null, userInfo, roles);
                       }
                   })
               }
           })
       }
   })
};

每个函数都有一个参数,该参数是另一个函数,该函数被调用时带有一个参数,该参数是前一个操作的响应.

太多的人仅仅通过阅读上面的句子就会经历大脑冻结. 拥有数百个类似代码块的应用程序将给维护代码的人员带来更多麻烦, even if 的y wrote it 的mselves.

这个例子会变得更加复杂 数据库.将getRoles is ano的r function that has nested callbacks.

const 将getRoles = function (username, callback){
   数据库.connect((连接) => {
       连接.query('get roles sql', (result) => {
           callback(null, result);
       })
   });
};

除了代码难以维护之外, 的 DRY principle has absolutely no value in this case. 错误处理, 例如, 在每个函数中重复,并且从每个嵌套函数调用主回调.

More complex 异步hronous JavaScript operations, such as looping through 异步hronous calls, is an even bigger challenge. 实际上,没有简单的方法可以通过回调来实现这一点. This is why JavaScript 承诺 libraries like 蓝知更鸟Q 得到了如此多的关注. 它们提供了一种对异步请求执行通用操作的方法,这是语言本身没有提供的.

That’s where native JavaScript的承诺 come in.

JavaScript的承诺

承诺 were 的 next logical step in escaping 回调地狱. This method did not remove 的 use of callbacks, 但是它使JavaScript中异步函数的链接变得简单明了 简化代码, making it much easier to read.

Illustration: 异步 JavaScript的承诺 diagram

With 承诺 in place, 我们的异步JavaScript示例中的代码看起来像这样:

const verifyUser = function(username, password) {
   数据库.verifyUser(username, password)
       .然后(userInfo => 数据库.将将getRoles(用户信息)
       .然后(rolesInfo => 数据库.logAccess (rolesInfo))
       .然后(finalResult => {
           //do whatever 的 'callback' would do
       })
       .抓((err) => {
           //do whatever 的 error h和ler needs
       });
};

为了实现这种简单性,示例中使用的所有函数都必须是 Promisified. Let’s take a look at how 的 将getRoles method would be updated to return a 承诺:

const 将getRoles = function (username){
   return new 承诺((解决, 拒绝) => {
       数据库.connect((连接) => {
           连接.query('get roles sql', (result) => {
               解决(结果);
           })
       });
   });
};

We have modified 的 method to return a 承诺, with two callbacks, 和 的 承诺 itself performs actions from 的 method. 现在, 解决拒绝 callbacks will be mapped to 承诺.然后承诺.抓 方法分别.

You may notice that 的 将getRoles 方法内部仍容易出现金字塔厄运现象. 这是由于数据库方法的创建方式,因为它们不返回 承诺. If our 数据库 access methods also returned 承诺将getRoles method would look like 的 following:

const 将getRoles = new function (userInfo) {
   return new 承诺((解决, 拒绝) => {
       数据库.connect ()
           .然后((连接) => 连接.query('get roles sql'))
           .然后((result) => 解决(result))
           .抓住(拒绝)
   });
};

Approach 3: Async/Await

随着“承诺”的引入,厄运金字塔得到了显著的缓解. 然而,我们仍然必须依赖传递给的回调 .然后.抓 a的方法 承诺.

承诺为JavaScript中最酷的改进之一铺平了道路. ECMAScript 2017 在JavaScript的承诺之上添加了语法糖,形式是 异步等待 语句.

They allow us to write 承诺-based code as if it were synchronous, but without blocking 的 main thread, as this code sample demonstrates:

const verifyUser = 异步函数(username, password){
   尝试{
       const userInfo = 等待 数据库.verifyUser(username, password);
       const rolesInfo = 等待 数据库.将将getRoles(用户信息);
       const logStatus = 等待 数据库.logAccess(用户信息);
       返回用户信息;
   }捕捉(e) {
       //h和le errors as needed
   }
};

等待 承诺 to 解决 is allowed only within 异步 functions which means that verifyUser had to be defined using 异步函数.

However, once this small change is made you can 等待 任何 承诺 without additional changes in o的r methods.

Async JavaScript——期待已久的承诺解决方案

异步函数是JavaScript中异步编程发展的下一个合乎逻辑的步骤. 它们将使您的代码更干净,更易于维护. Declaring a function as 异步 will ensure that it always returns a 承诺 so you don’t have to worry about that 任何more.

什么 异步 为什么你应该开始使用JavaScript 异步 今天的功能?

  1. The resulting code is much cleaner.
  2. 错误处理 is much simpler 和 it relies on 试一试/ just like in 任何 o的r synchronous code.
  3. Debugging is much simpler. Setting a breakpoint inside a .然后 block will not move to 的 next .然后 because it only steps through synchronous code. But, you can step through 等待 calls as if 的y were synchronous calls.

Underst和ing 的 basics

  • What are 异步 和 等待?

    Async/等待语句是在JavaScript的承诺之上创建的语法糖. 它们允许我们编写基于承诺的代码,就好像它是同步的一样, but without blocking 的 main thread.

  • What is 回调地狱?

    在JavaScript中, 回调地狱是代码中的一种反模式,它是异步代码结构不良的结果. 当程序员试图在基于异步回调的JavaScript代码中强制使用可视化的自顶向下结构时,通常会看到这种情况.

  • What are JavaScript promises?

    JavaScript中的promise就像一个占位符值,期望最终解析为最终成功的结果值或失败的原因.

Hire a Toptal expert on this topic.
现在雇佣
Demir Selmanovic

Demir Selmanovic

验证专家 在工程
24 的经验

萨拉热窝,波斯尼亚-黑塞哥维那联邦,波斯尼亚-黑塞哥维那

Member since July 8, 2014

作者简介

Demir是一名开发人员和项目经理,在广泛的软件开发角色方面拥有超过15年的专业经验.

作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

World-class articles, delivered weekly.

Subscription implies consent to our 隐私政策

World-class articles, delivered weekly.

Subscription implies consent to our 隐私政策

Toptal开发者

加入总冠军® 社区.