作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Demir是一名开发人员和项目经理,在广泛的软件开发角色方面拥有超过15年的专业经验.
编写成功的web应用程序的关键之一是能够在每个页面上进行数十次AJAX调用.
This is a typical asynchronous programming challenge, and how you choose to deal with asynchronous calls will, in large part, make or break your app, and by extension potentially your entire startup.
在很长一段时间里,在JavaScript中同步异步任务是一个严重的问题.
This challenge is affecting back-end developers using Node.js 与使用任何JavaScript框架的前端开发人员一样多. Asynchronous programming is a part of our everyday work, 但这一挑战往往被轻视,没有在适当的时候加以考虑.
第一个也是最直接的解决方案是 nested functions as callbacks. This solution led to something called callback hell, and too many applications still feel the burn of it.
Then, we got Promises. This pattern made the code a lot easier to read, 但这与“不要重复自己”(DRY)原则相去甚远. 仍然有很多情况下,您必须重复相同的代码片段才能正确地管理应用程序的流. 最新的添加,以async/await JavaScript语句的形式,终于完成了 asynchronous code in JavaScript as easy to read and write as any other piece of code.
让我们看一下这些解决方案的示例,并回顾一下JavaScript中异步编程的发展历程.
To do this, 我们的异步JavaScript教程将研究一个执行以下步骤的简单任务:
同步这些调用的古老解决方案是通过嵌套回调. 对于简单的异步JavaScript任务来说,这是一种不错的方法, but wouldn’t scale because of an issue called callback hell.
这三个简单任务的代码看起来像这样:
const verifyUser =函数(用户名,密码,回调){
dataBase.verifyUser(username, password, (error, userInfo) => {
if (error) {
callback(error)
}else{
dataBase.getRoles(username, (error, roles) => {
if (error){
callback(error)
}else {
dataBase.logAccess(username, (error) => {
if (error){
callback(error);
}else{
callback(null, userInfo, roles);
}
})
}
})
}
})
};
每个函数都有一个参数,该参数是另一个函数,该函数被调用时带有一个参数,该参数是前一个操作的响应.
太多的人仅仅通过阅读上面的句子就会经历大脑冻结. 拥有数百个类似代码块的应用程序将给维护代码的人员带来更多麻烦, even if they wrote it themselves.
这个例子会变得更加复杂 database.getRoles
is another function that has nested callbacks.
const getRoles = function (username, callback){
database.connect((connection) => {
connection.query('get roles sql', (result) => {
callback(null, result);
})
});
};
除了代码难以维护之外, the DRY principle has absolutely no value in this case. Error handling, for example, 在每个函数中重复,并且从每个嵌套函数调用主回调.
More complex asynchronous JavaScript operations, such as looping through asynchronous calls, is an even bigger challenge. 实际上,没有简单的方法可以通过回调来实现这一点. This is why JavaScript Promise libraries like Bluebird and Q got so much traction. 它们提供了一种对异步请求执行通用操作的方法,这是语言本身没有提供的.
That’s where native JavaScript Promises come in.
Promises were the next logical step in escaping callback hell. This method did not remove the use of callbacks, 但是它使JavaScript中异步函数的链接变得简单明了 simplified the code, making it much easier to read.
With Promises in place, 我们的异步JavaScript示例中的代码看起来像这样:
const verifyUser = function(username, password) {
database.verifyUser(username, password)
.then(userInfo => dataBase.getRoles(userInfo))
.then(rolesInfo => dataBase.logAccess(rolesInfo))
.then(finalResult => {
//do whatever the 'callback' would do
})
.catch((err) => {
//do whatever the error handler needs
});
};
为了实现这种简单性,示例中使用的所有函数都必须是 Promisified. Let’s take a look at how the getRoles
method would be updated to return a Promise
:
const getRoles = function (username){
return new Promise((resolve, reject) => {
database.connect((connection) => {
connection.query('get roles sql', (result) => {
resolve(result);
})
});
});
};
We have modified the method to return a Promise
, with two callbacks, and the Promise
itself performs actions from the method. Now, resolve
and reject
callbacks will be mapped to Promise.then
and Promise.catch
methods respectively.
You may notice that the getRoles
方法内部仍容易出现金字塔厄运现象. 这是由于数据库方法的创建方式,因为它们不返回 Promise
. If our database access methods also returned Promise
the getRoles
method would look like the following:
const getRoles = new function (userInfo) {
return new Promise((resolve, reject) => {
database.connect()
.then((connection) => connection.query('get roles sql'))
.then((result) => resolve(result))
.catch(reject)
});
};
随着“承诺”的引入,厄运金字塔得到了显著的缓解. 然而,我们仍然必须依赖传递给的回调 .then
and .catch
methods of a Promise
.
承诺为JavaScript中最酷的改进之一铺平了道路. ECMAScript 2017 在JavaScript的Promises之上添加了语法糖,形式是 async
and await
statements.
They allow us to write Promise
-based code as if it were synchronous, but without blocking the main thread, as this code sample demonstrates:
const verifyUser = async function(username, password){
try {
const userInfo = await dataBase.verifyUser(username, password);
const rolesInfo = await dataBase.getRoles(userInfo);
const logStatus = await dataBase.logAccess(userInfo);
return userInfo;
}catch (e){
//handle errors as needed
}
};
Awaiting Promise
to resolve is allowed only within async
functions which means that verifyUser
had to be defined using async function
.
However, once this small change is made you can await
any Promise
without additional changes in other methods.
异步函数是JavaScript中异步编程发展的下一个合乎逻辑的步骤. 它们将使您的代码更干净,更易于维护. Declaring a function as async
will ensure that it always returns a Promise
so you don’t have to worry about that anymore.
What does async
为什么你应该开始使用JavaScript async
functions today?
try
/catch
just like in any other synchronous code..then
block will not move to the next .then
because it only steps through synchronous code. But, you can step through await
calls as if they were synchronous calls.Async/await语句是在JavaScript Promises之上创建的语法糖. 它们允许我们编写基于承诺的代码,就好像它是同步的一样, but without blocking the main thread.
In JavaScript, 回调地狱是代码中的一种反模式,它是异步代码结构不良的结果. 当程序员试图在基于异步回调的JavaScript代码中强制使用可视化的自顶向下结构时,通常会看到这种情况.
JavaScript中的promise就像一个占位符值,期望最终解析为最终成功的结果值或失败的原因.
萨拉热窝,波斯尼亚-黑塞哥维那联邦,波斯尼亚-黑塞哥维那
Member since July 8, 2014
Demir是一名开发人员和项目经理,在广泛的软件开发角色方面拥有超过15年的专业经验.
World-class articles, delivered weekly.
World-class articles, delivered weekly.
Join the Toptal® community.