async/await 是以更舒适的方式使用 promise 的一种特殊语法。
在 Salesforce 中,如果你有开发过 lwc 相关的组件,想必应该使用过 Async/Await
语法。其实在 JavaScript 中做异步开发时,我们通常会毫不犹豫的使用 Async/Await
. 不管是并发还是串行,Async/Await
都能处理的很好,而且还保证了代码的可读性。本篇内容主要是我根据一些资料和官方文档来阐述对 Async/Await
语法的一些理解,如有叙述不对的地方,欢迎指正。
什么是 async/await?
JavaScript 中的 async/await
是 AsyncFunction 特性中的关键字。目前为止,除了 IE 之外,常用浏览器和 Node.js (v7.6+) 都已经支持该特性。具体支持情况可以在参考 这里。
async/await
是一种建立在 Promise 之上的编写异步或非阻塞代码的新方法 (Generator 的语法糖), 普遍认为是 JS 异步操作的最优雅的解决方案。相对于 Promise
和回调,它的可读性和简洁度都更高。只要 function 标记为 async
, 就表示函数里面可以写 await 的同步语法,而 await 顾名思义就是「等待」,它会确保一个 promise
都解決 ( resolve
) 或出错 ( reject
) 后才会进行下一步,当 async function 的内容全部执行结束,会返回一个 promise
, 表示后续代码可以使用 .then
语法來连接,基本的代码就像下面这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function a(){
await b();
..... // 等 b() 完成后才会执行
await c();
..... // 等 c() 完成后才会执行
await new Promise(resolve=>{
.....
});
..... // 上方的 promise 完成后才会执行
}
a();
a().then(()=>{
..... // 等 a() 完成后接著执行
});
async 的作用?
我们先看如下代码的输出结果是什么:
1
2
3
4
5
6
async function testAsync() {
return "hello async";
}
const result = testAsync();
console.log(result); //Promise {<fulfilled>: 'hello async'}
所以,async 函数返回的是其实一个 Promise 对象。从文档中也可以得到这个信息。async 函数会返回一个 Promise 对象。如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。
async 函数返回的是一个 Promise 对象,所以在最外层不用 await 获取其返回值的情况下,我们可以用原来的方式:then() 链来处理这个 Promise 对象,就像这样:
1
2
3
testAsync().then(v => {
console.log(v); // 输出 hello async
});
await 在等待什么呢?
根据 MDN 文档 上写的:
1
[return_value] = await expression;
await 等待的是一个表达式,那么表达式,可以是一个常量 ,变量,promise, 函数等,换句话说,就是没有特殊限定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function getSomething() {
return "something";
}
async function testAsync() {
return Promise.resolve("hello async");
}
async function test() {
const v1 = await getSomething();
const v2 = await testAsync();
console.log(v1, v2);
}
test(); // something hello async
为什么 await 关键词只能在 async 函数中用?
await 操作符等的是一个返回的结果,如果它等到的是一个 Promise 对象,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。
async/await 的优势
假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们先用 setTimeout 来模拟异步操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function takeLongTime(n) {
return new Promise(resolve => {
setTimeout(() => resolve(n + 200), n);
});
}
function step1(n) {
console.log(`step1 with ${n}`);
return takeLongTime(n);
}
function step2(n) {
console.log(`step2 with ${n}`);
return takeLongTime(n);
}
function step3(n) {
console.log(`step3 with ${n}`);
return takeLongTime(n);
}
现在用 Promise 方式来实现这三个步骤的处理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function doIt() {
console.time("doIt");
const time1 = 300;
step1(time1)
.then(time2 => step2(time2))
.then(time3 => step3(time3))
.then(result => {
console.log(`result is ${result}`);
console.timeEnd("doIt");
});
}
doIt();
//step1 with 300
// step2 with 500
// step3 with 700
// result is 900
// doIt: 1512.908935546875 ms
如果用 async/await 来实现呢,会是这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function doIt() {
console.time("doIt");
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time2);
const result = await step3(time3);
console.log(`result is ${result}`);
console.timeEnd("doIt");
}
doIt();
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
// doIt: 1512.2080078125 ms
结果和之前的 Promise 实现是一样的,但是这个代码看起来清晰得多,几乎跟同步代码一样。
思考:为什么需要异步编程?
JavaScript 是单线程的,就是必须等待上一个任务执行完才能执行下一个任务,这种执行模式叫:同步
.
但是随着业务复杂度的提升,很多情况下有处理高并发 (单位时间内极大的访问量) 和 I/O 密集场景 (ps: I/O 操作往往非常耗时,所以异步的关键在于解决 I/O 耗时问题), 如果采用同步编程,问题就来了,服务器处理一个 I/O 请求需要大量的时间,后面的请求都将排队,造成浏览器端的卡顿。异步编程
能解决这个问题。