JavaScript ES 2017: 通过示例学习Async/Await
预备知识
ES 2017新增了Asynchronous函数。 在JavaScript中,Async函数本质上是一种处理异步代码的比较简洁的方法。 为了理解这些是什么,以及它们是如何工作的,我们首先需要了解Promise。
如果你不知道Promise是什么,那么你应该先阅读我发表的关于Promise的这篇文章。 在理解Promise之前,你是不会理解JavaScript中的Async/Await。
Async/Await是什么?
- JavaScript编写异步代码的最新方法
- 它是非阻塞的(就像Promise和回调)。
- Async/Await是为了简化使用和编写链接promise的过程。
- Async/Await返回一个Promise。 如果函数抛出错误,Promise将被拒绝。 如果函数返回值,Promise将被解析。
语法
编写异步函数非常简单。 你只需要在function之前添加async关键字:
// 普通函数
function add(x,y){
return x + y;
}
// 异步函数
async function add(x,y){
return x + y;
}
Await
异步函数可以使用await表达式。 这将暂停async函数,并等待Promise解决之后再继续。
示例时间
说够了。 要了解这一切意味着什么,让我们看一个例子! 首先,我们将使用promise来创建一些代码。 一旦我们有了一些工作,我们将使用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的函数。 这个函数会以一个数字作为输入,并且在两秒钟后处理,数字加倍。
我们可以调用我们的函数,并传进10来试下。 为了做到这一点,我们将在传入10时调用函数。然后,在promise处理后,我们获取返回值并输出到控制台。 这是这样的:
doubleAfter2Seconds(10).then((r) => {
console.log(r);
});
真棒!
但是如果我们想通过函数执行不同的值并累加结果? 不幸的是,我们不能将我们的调用加在一起并记录下来:
let sum = doubleAfter2Seconds(10)
+ doubleAfter2Seconds(20)
+ doubleAfter2Seconds(30);
console.log(sum);
// undefined
上面的代码的问题是它实际上没有等待我们的promise处理就输出到控制台了。
一个可能的解决方案是建立一个promise链。 为此,我们创建一个名为addPromise的新函数。 我们的函数获得输入值,并将返回一个Promise。 以下是示例代码的样子:
function addPromise(x){
return new Promise(resolve => {
// 此处为代码
// 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。两秒钟后,返回20给一个变量。
-
我们再次调用doubleAfter2Seconds,这次传递的值为20.两秒钟后,返回40给变量b。
-
我们最后一次调用doubleAfter2Seconds,这次传递的值是30.两秒钟后,返回60给变量c。
-
最后,我们以10 + 20 + 40 + 60或130的值来处理Promise。
当我们把所有的代码放在一起时,看起来像这样:
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);
})
})
})
});
}
addPromise(10).then((sum) => {
console.log(sum);
});
从Promise切换到Async/Await
真棒! 现在让我们看看使用Async/Await编写上面的代码将更容易!
删除addPromise函数,并创建一个名为addAsync的新函数。 这个函数与我们的addPromise具有完全相同的用途。 在创建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;
}
这里是完整的代码:
function doubleAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x * 2);
}, 2000);
});
}
async function addAsync(x) {
const a = await doubleAfter2Seconds(10);
const b = await doubleAfter2Seconds(20);
const c = await doubleAfter2Seconds(30);
return x + a + b + c;
}
addAsync(10).then((sum) => {
console.log(sum);
});
正如你所看到的,我们仍然使用doubleAfter2Seconds函数。 同样,我们调用addAsync()函数并传入值10。完成后记录结果值。 让我们一步步地过:
-
首先我们调用addAsync(10)传入10。
-
接着,我们在第10行获取a的值。由于使用了关键字await,函数暂停了两秒钟,等待Promise处理。 一旦Promise处理了,得到a = 20。
const a = await doubleAfter2Seconds(10);
- 接着,我们在第11行获取b的值。由于使用了关键字await,函数在等待Promise处理的时候暂停了两秒钟。 一旦Promise处理了,得到b = 40。
const b = await doubleAfter2Seconds(20);
- 接着,我们在第12行获取c的值。由于使用了关键字await,函数在等待Promise处理的时候暂停了两秒钟。 一旦Promise处理了,得到c = 60。
const c = await doubleAfter2Seconds(30);
-
最后,我们可以返回x + a + b + c的值。 因为以10作为参数,所以返回10 + 20 + 40 + 60的值。
-
六秒钟后,console.log(sum)终于运行了。 传入10 + 20 + 40 + 60的值,并将130输出到控制台。
就是这样! 你刚刚用JavaScript中创建了一个异步函数!
正如你所看到的,由于异步函数返回一个Promise,所以它们可以很容易地与Promises交替使用。 当我们使用Async/Await而不是长的Promise链时,我们的代码也更清晰易读。