实战学习JavaScript ES 2017: Async/Await
用一个简单的例子解释Async/Await

前置知识
在ES 2017中引入了异步函数(Asynchronous functions),简单来说在javascript中异步函数是一种更简约的处理异步代码的方式。为了准确地理解他们的本质以及运作方式,首先我们需要理解Promises。
如果你不知道什么是Promises,你应该首先阅读这篇关于promises的文章 ,只有完全理解了 Promises后才能理解Async/Await。
什么是Async/Await?
-
JavaScript中最新的书写异步代码的方式
-
非阻塞式(就像promises和callback一样)
-
Async/Await的目的是为了简化promises的链式代码的书写
-
异步函数返回一个Promise,如果函数抛出一个错误,表明Promise被拒绝/执行失败,如果函数返回了值,表明Promise被接受/执行成功。
语法
写异步函数非常简单,你只需要在function前面添加关键字 async 即可:
**// Normal Function**
function add(x,y){
return x + y;
}
**// Async Function**
async function add(x,y){
return x + y;
}
Await关键字
异步函数使用await关键字,用于暂停async函数并等待Promise解析来进行下一步。
举栗子时间
话不多说,让我来看一个例子!首先我们使用promises来写代码,一旦运行成功,我们用async/await重写我们的函数,来看看它是如何的简单!
如果你是用Google Chrome浏览器来实验代码的话,请确保将代码输入开发终端中,你可以通过Ctrl+Shift+J (Windows / Linux) 或者 Cmd+Opt+J (Mac)来开启开发终端。
代码如下:
function doubleAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x * 2);
}, 2000);
});
}
在这段代码中我们创建了一个函数doubleAfter2Seconds,这个函数接受一个数值作为输入参数,然后过2秒之后将这个数值翻倍。
我们可以输入数字10来执行这个函数,等Promise执行成功,我们将返回值记录下来并输出到终端,执行代码如下:
doubleAfter2Seconds(10).then((r) => {
console.log(r);
});
棒极了!
但是,如果我们想通过我们的函数运行几个不同的值并添加结果呢?不幸的是我们不能单纯地调用这些函数并他们加起来进行输出。
let sum = doubleAfter2Seconds(10)
+ doubleAfter2Seconds(20)
+ doubleAfter2Seconds(30);
console.log(sum);
// undefined
上面的代码存在的问题是它实际上没等待Promises解析成功就在终端进行了输出。
一种解决方法是链式调用promise。为了达到这一目的我们创建一个新函数 addPromise。这个函数接受一个输入值,并返回一个Promise对象。示例代码如下:
function addPromise(x){
return new Promise(resolve => {
// Code goes here...
// resolve()
});
}
棒极了!现在我们将这个函数添加到之前的函数 doubleAfter2Seconds中去,一旦完成后我们就可以得到叠加值。在这个例子中,我们返回x + 2*a + 2*b + 2*c,代码如下:
function addPromise(x){
return new Promise(resolve => {
doubleAfter2Seconds(10).then((a) => {
doubleAfter2Seconds(20).then((b) => {
doubleAfter2Seconds(30).then((c) => {
resolve(x + a + b + c);
})
})
})
});
}
让我们一行一行地回顾代码
- 首先,我们创建了函数
addPromise,这个函数接受一个输入值。 -
接着,我们返回了新Promise对象,为了简化代码,我们并没有处理失败/错误。
- 再下来,我们调用
doubleAfter2Seconds函数,传入值10,2秒之后,结果20储存在变量a中返回。 - 我们对
doubleAfter2Seconds函数再次进行调用,这次我们传入值20,2秒之后,将结果40储存在变量b中返回。 - 我们最后一次调用
doubleAfter2Seconds,这次传入传值30,2秒之后,将结果60储存在变量c中返回。 - 最后,我们解析Promise成功,并返回值
10 + 20 + 40 + 60或者130。
我们把所有的代码整合起来就是这样:
function doubleAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x * 2);
}, 2000);
});
}
function addPromise(x){
return new Promise(resolve => {
doubleAfter2Seconds(10).then((a) => {
doubleAfter2Seconds(20).then((b) => {
doubleAfter2Seconds(30).then((c) => {
resolve(x + a + b + c);
})
})
})
});
}
从 Promises 走向 Async/Await.
棒极了!现在我们来看看使用Async/Await来实现上面的代码是多简单。
去掉函数addPromise,穿件一个新函数addAsync,这个新函数实现的功能与addPromise一样,在写新函数是我们使用关键字async,代码如下:
async function addAsync(x) {
// code here...
}
这样我们就创建了一个异步函数,我们可以使用关键字 await来等待Promise进行解析,如此的简单:
async function addAsync(x) {
const a = await doubleAfter2Seconds(10);
const b = await doubleAfter2Seconds(20);
const c = await doubleAfter2Seconds(30);
return x + a + b + c;
}
而这就是全部的代码。
如您所见,我们依旧保留了doubleAfter2Seconds函数,只是调用addAsync()函数并传入值10,然后输出返回值。让我们一步一步解释这段代码:
- 首先我们调用
addAsync(10),并传入值10。 - 然后我们获取
a的值,因为我们使用了关键字await,我们的函数自动暂停2秒钟以解析promise,一旦解析成功,a = 20。
`const a = await` doubleAfter2Seconds`(10);`
- 接着我们获取
b的值,因为我们使用了关键字await,我们的函数自动暂停2秒钟以等待promise解析,一旦解析成功,b = 40。
`const b = await` doubleAfter2Seconds`(20);`
-
- 接着我们获取
c的值,因为我们使用了关键字await,我们的函数自动暂停2秒钟以等待promise解析,一旦解析成功,c = 60。
- 接着我们获取
`const c = await` doubleAfter2Seconds`(30);`
- 最后,我们返回
x + a + b + c,因为我们传入了10作为输入值,我们将返回10 + 20 + 40 + 60。 - 整整6秒种后,代码
console.log(sum)被执行,传入参数10 + 20 + 40 + 60,结果就在终端中输出130。
这是这样!你已经学会在Javascript中创建异步函数了!
如你所见,异步函数返回Promise对象,所以可以很方便地处理Promises对象,使用异步函数取代长长的链式promise也让我们的代码也变得更加清晰易读。