async & await
Alphawq asyncawait
# 语法糖
Generator 函数的语法糖。有更好的语义、更好的适用性、返回值是 Promise。
# 例子 1
let a = 0
let b = async () => {
a = a + (await 10)
console.log('2', a) // -> '2' 10
a = (await 10) + a
console.log('3', a) // -> '3' 20
}
b()
a++
console.log('1', a) // -> '1' 1
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 首先函数 b 先执行,在执行到 await 语句的时候,函数 b 会先暂停执行,并保留当前堆栈中 a 的值 0
- 因为 await 是一个异步操作,会立即返回一个 pending 状态的 promise,并暂时返回执行代码的控制权,所以函数 b 会被暂停执行,
- 此时函数外的代码得以继续执行,所以会执行 a++ 操作,然后输出
'1' 1
,此时 a 的值是 1 - 此时同步代码执行完成,开始执行异步代码,代码从刚才暂停的地方恢复,将保留的当时执行堆栈里的变量取出,所以此时 a 还是 0
- await 返回的 promise 有了结果 10,所以
a = a + 10
,继续执行输出'2' 10
- 然后继续
a = a + 10
,继续执行输出'3' 20
- 程序结束
如果改成 await b()
呢?顶层 await (opens new window)
'2' 10
'3' 20
'1' 21
1
2
3
2
3
await
只会阻塞同级代码的执行
# 例子 2
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout')
}, 0)
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})
console.log('script end')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
执行结果如下:
'script start'
'async1 start'
'async2'
'promise1'
'script end'
'async1 end'
'promise2'
'setTimeout'
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 分析
- 首先输出
'script start'
- 遇到 setTimeout,放入到 Task 队列,主进程继续执行其他代码
- 紧接着 async1 函数执行,输出
'async1 start'
- 然后 async2 函数被调用,输出
'async2'
,async2 执行完毕,并返回一个pending
的promise
- 由于 await 关键字的存在,返回的 promise 的结果回调被加入到微任务队列中去,且 async1 暂停执行,移交控制权
- 传给 Promise 构造器的函数立即执行,输出
'promise1'
,然后立即 resolve,then 方法中的回调被加入到微任务队列 - 输出
'script end'
,主线程上的任务执行完毕。开始检查微任务队列,此时微任务队列中有两个任务,然后全部取出并执行它们的回调 - 依次输出
'async1 end'
'promise2'
。 - 微任务队列被清空,检查 Task 队列,存在已经到期定时器任务,取出并执行,输出
'setTimeout'
# 原理
async await 的原理其实就是一个自动执行的 generator 语法糖,跟之前说的 thunk 函数的原理一样
function thunk(gen) {
let g = gen()
function next(data) {
let { value, done } = g.next(data)
if (done === true) return data
value.then((res) => next(res))
}
next()
}
thunk(gen)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 总结
- async 关键字只是用来标记函数是一个异步函数,内部如果没有 await 关键字,那函数内部将从上倒下依次执行完
- await 关键字相当于 yield,遇到 await 首先会对它后面的表达式或者函数进行求值,并返回一个 pending 的 promise,将回调放入微任务队列,函数暂停执行,保留当前上下文的堆栈信息,并移交控制权
- 待主线程其他任务执行完毕之后,从 await 处恢复并继续执行