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

Demir Selmanovic

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

工作经验

24

分享

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

这是一个典型的异步编程挑战, 以及如何选择处理异步调用, 很大程度上, 成就或破坏你的应用, 延伸到整个创业公司.

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

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

异步JavaScript简史

第一个也是最直接的解决方案是 嵌套函数作为回调函数. 这个解决方案产生了一种叫做 回调地狱,还有太多的应用程序仍然感受到它的灼热.

然后,我们得到 承诺. 这种模式使代码更容易阅读, 但这与“不要重复自己”(DRY)原则相去甚远. 仍然有很多情况下,您必须重复相同的代码片段才能正确地管理应用程序的流. 最新的添加,以异步/等待 JavaScript语句的形式,终于完成了 JavaScript异步代码 像其他代码一样易于阅读和编写.

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

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

  1. 验证用户的用户名和密码.
  2. 获取用户的应用程序角色.
  3. 记录用户的应用程序访问时间.

方法1:回调地狱(“末日金字塔”)

同步这些调用的古老解决方案是通过嵌套回调. 对于简单的异步JavaScript任务来说,这是一种不错的方法, 但由于一个叫做 回调地狱.

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

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

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

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

太多的人仅仅通过阅读上面的句子就会经历大脑冻结. 拥有数百个类似代码块的应用程序将给维护代码的人员带来更多麻烦, 即使是他们自己写的.

这个例子会变得更加复杂 数据库.将getRoles 另一个函数有嵌套回调吗.

const 将getRoles =函数(用户名,回调){
   数据库.connect((连接) => {
       连接.query('get roles sql', (result) => {
           回调(零,结果);
       })
   });
};

除了代码难以维护之外, DRY原则在这种情况下绝对没有价值. 错误处理, 例如, 在每个函数中重复,并且从每个嵌套函数调用主回调.

更复杂的异步JavaScript操作, 例如通过异步调用进行循环, 是更大的挑战吗. 实际上,没有简单的方法可以通过回调来实现这一点. 这就是为什么JavaScript承诺库喜欢 蓝知更鸟Q 得到了如此多的关注. 它们提供了一种对异步请求执行通用操作的方法,这是语言本身没有提供的.

这就是原生JavaScript承诺的用武之地.

JavaScript的承诺

承诺 逃离回调地狱的下一个合乎逻辑的步骤是什么. 此方法没有删除回调的使用, 但是它使JavaScript中异步函数的链接变得简单明了 简化代码使它更容易阅读.

插图:异步JavaScript承诺图

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

const verifyUser = function(用户名,密码){
   数据库.verifyUser(用户名、密码)
       .然后(userInfo => 数据库.将将getRoles(用户信息)
       .然后(rolesInfo => 数据库.logAccess (rolesInfo))
       .然后(finalResult => {
           //执行'callback'将要执行的操作
       })
       .抓((err) => {
           //执行错误处理程序所需的任何操作
       });
};

为了实现这种简单性,示例中使用的所有函数都必须是 Promisified. 让我们来看看 将getRoles 方法将被更新为返回 承诺:

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

我们修改了这个方法以返回a 承诺,有两个回调,以及 承诺 自身执行方法中的操作. 现在, 解决拒绝 回调函数将被映射到 承诺.然后承诺.抓 方法分别.

你可能会注意到 将getRoles 方法内部仍容易出现金字塔厄运现象. 这是由于数据库方法的创建方式,因为它们不返回 承诺. 如果我们的数据库访问方法也返回 承诺将getRoles 方法如下所示:

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

方法3:Async/Await

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

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

他们允许我们写作 承诺基于的代码,就好像它是同步的, 但是不会阻塞主线程, 如下代码示例所示:

const verifyUser = 异步函数(用户名,密码){
   尝试{
       const userInfo =等待数据库.verifyUser(用户名、密码);
       const rolesInfo =等待数据库.将将getRoles(用户信息);
       const logStatus =等待数据库.logAccess(用户信息);
       返回用户信息;
   }捕捉(e) {
       //根据需要处理错误
   }
};

等待 承诺 只有在内心才允许解决 异步 函数的意思是 verifyUser 必须使用 异步函数.

然而,一旦做了这个小小的改变,你就可以 等待 任何 承诺 无需对其他方法进行额外更改.

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

异步函数是JavaScript中异步编程发展的下一个合乎逻辑的步骤. 它们将使您的代码更干净,更易于维护. 将函数声明为 异步 将确保它总是返回 承诺 所以你不用再担心这个了.

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

  1. 生成的代码要干净得多.
  2. 错误处理要简单得多,它依赖于 试一试/ 就像在其他同步代码中一样.
  3. 调试要简单得多. 类中设置断点 .然后 块不会移动到下一个 .然后 因为它只步进同步代码. 但是,你可以跳过 等待 调用,就好像它们是同步调用一样.

了解基本知识

  • 什么是异步和等待?

    Async/等待语句是在JavaScript的承诺之上创建的语法糖. 它们允许我们编写基于承诺的代码,就好像它是同步的一样, 但是不会阻塞主线程.

  • 什么是回调地狱?

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

  • JavaScript的承诺是什么?

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

聘请Toptal这方面的专家.
现在雇佣
Demir Selmanovic的头像
Demir Selmanovic

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

成员自 2014年7月8日

作者简介

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

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

工作经验

24

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.