前言
如果你之前跟我一样始终对async
`await` 相熟又生疏的话(相熟是可能每天都在用,生疏是针对一些组合题又丈二和尚摸不着头脑),无妨能够边看边练,总结法则,置信会逐步清晰并有所得。本文对每个案例都详细描述了代码的执行流程,如有不妥欢送斧正。
async 函数 return 值
async
函数默认会返回一个 Promise
对象,不论最初函数有没有 return
值。然而针对具体的返回值状况,实际上体现会有所不同,上面别离看看。
return 值为一般值
这里的一般值是指根底类型值(Number
、String
和 Boolean
等)和非 thenable
和非 Promise
的值
async function foo() {return 'foo'}
foo().then(() => console.log('a'))
Promise.resolve()
.then(() => console.log('b'))
.then(() => console.log('c'))
// 输入后果:a b c
很简略,不出意外输入 a b c
,也就是async
函数在执行实现后是没有期待的。
foo()
执行实现没有期待,遇到then
将console.log('a')
放入微工作队列;- 持续往下执行
Promise.resolve()
,遇到then
将console.log('b')
入队,以后同步工作全副执行实现; - 开始执行微工作队列,首先取出并执行
console.log('a')
输入a
; - 而后取出并执行
console.log('b')
输入b
,此时遇到then
将console.log('c')
入队; -
最初取出并执行
console.log('c')
输入c
,至此微工作队列清空,代码执行完结;return 值为 thenable
所谓值为
thenable
是指定义了then
办法的对象,能够是一个字面对象,也能够是一个Class
实例。class Bar {then(resolve) {resolve() console.log('then') } } async function foo() {// return new Bar() return {then(resolve, reject) {resolve() console.log('then') } } } foo().then(() => console.log('a')) Promise.resolve() .then(() => console.log('b')) .then(() => console.log('c')) // 输入后果:then b a c
怎么程序不一样了呢?
如果
async
函数的返回值是一个thenable
,等同于生成一个Promise
,在foo
函数执行实现,并且Promise
状态变更(resolve
或者reject
)后,还要等 1 个then
的时长 foo()
返回thenable
值,执行then
办法,Promise
状态变更,执行console.log('then')
输入then
,期待 1 个then
时长;- 持续往下执行
Promise.resolve()
,遇到then
将console.log('b')
放入微工作队列,以后同步工作执行实现; - 开始执行微工作队列,首先取出并执行
console.log('b')
输入b
,以后微工作队列清空; - 此时步骤 1 期待时长到期,遇到
then
将console.log('a')
放入队列,取出执行输入a
; - 持续步骤 3 遇到
then
将console.log('c')
放入队列,取出执行输入c
,至此微工作队列清空,代码执行完结;
这里如果 foo
函数返回的 thenable
办法的状态没有变更,则前面的 foo().then
将永远不会执行。
async function foo() {
return {then(resolve, reject) {console.log('then')
}
}
}
foo().then(() => console.log('a'))
Promise.resolve()
.then(() => console.log('b'))
.then(() => console.log('c'))
// 输入后果:then b c
return 值为 Promise
return
前面的值是 Promise
,比方 new Promise(resolve=>resolve())
和Promise.resolve
。
async function foo() {return Promise.resolve('foo')
}
foo().then(() => console.log('a'))
Promise.resolve()
.then(() => console.log('b'))
.then(() => console.log('c'))
.then(() => console.log('d'))
// 输入后果:b c a d
显著能够看出 async
函数执行完后提早了 2 个 then
时长。
foo()
返回Promise
值,Promise
状态变更,期待 2 个then
时长;- 持续往下执行
Promise.resolve()
,遇到then
将console.log('b')
放入微工作队列,以后同步工作执行实现; - 开始执行微工作队列,首先取出并执行
console.log('b')
输入b
,以后微工作队列清空; - 遇到
then
将console.log('c')
放入队列,取出执行输入c
; - 此时步骤 1 期待时长到期,遇到
then
将console.log('a')
放入队列,取出执行输入a
; - 持续步骤 4 遇到
then
将console.log('d')
放入队列,取出执行输入d
,至此微工作队列清空,代码执行完结;
综合上述体现能够总结出如下法则
await 表达式值
既然 async
函数返回值对代码执行程序有影响,那么 await
前面的表达式值是否也有影响呢?上面同样分为上述三种场景进行试验剖析
await 值为一般值
async function foo() {
await 'foo'
console.log('a')
}
foo().then(() => console.log('b'))
Promise.resolve()
.then(() => console.log('c'))
.then(() => console.log('d'))
// 输入后果:a c b d
能够判断,await
前面的表达式值如果是一般值,毋庸期待 then
时长。那么,为什么 b
会在 c
前面输入呢?
在
await
表达式有执行后果后,await
下一行到函数完结局部代码codex
能够看做搁置到微工作队列中,等同于Promise.resolve(await xxx).then(()=>codex)
,这里是伪代码,await
在工夫程序上等效于Promise.prototype.then
。
await 'foo'
执行实现后,console.log('a')
被增加到微工作队列;- 持续往下执行同步工作
Promise.resolve()
,遇到then
将console.log(c)
增加到微工作队列,以后同步工作执行实现; - 而后执行微工作队列中工作,取出并执行
console.log('a')
输入a
; - 此时
foo
函数执行实现,遇到then
将console.log('b')
入队; - 继续执行微工作队列中
console.log('c')
输入c
,此时遇到then
将console.log('d')
入队; -
最初顺次执行取出残余微工作,执行并输入
b
和d
,至此微工作队列清空,代码执行完结;await 值为 thenable
async function foo() { await {then(resolve) {resolve() console.log('then') } } console.log('a') } foo().then(() => console.log('b')) Promise.resolve() .then(() => console.log('c')) .then(() => console.log('d')) .then(() => console.log('e')) // 输入后果 then c a d b e
await
前面表达式值如果是thenable
,须要期待 1 个then
时长,才会去执行后续代码。 foo()
执行await
是一个thenable
,Promise
状态变更,执行同步代码console.log('then')
,输入then
,此时期待 1 个then
时长;- 持续往下执行同步工作
Promise.resolve()
,遇到then
将console.log('c')
退出到微工作队列,以后同步工作执行实现; - 开始执行微工作队列,取出并执行
console.log('c')
,输入c
,微工作队列清空; - 此时步骤 1 期待时长到期,将
await
后续代码console.log('a')
入队; - 持续步骤 3,遇到
then
将console.log('d')
入队,而后顺次取出console.log('a')
和console.log('d')
并执行,输入a
和d
; - 执行完
console.log('d')
遇到then
将console.log('e')
放入队列,取出执行,输入e
;
的确有点绕,咱们将 1 个 then
期待时长看做是下一个微工作从入队到执行实现出队的工夫就好。比方这里 c
工作执行实现,下一个工作 d
正筹备进入被 a
插了队。
await 值为 Promise
async function foo() {await Promise.resolve('foo')
console.log('a')
}
foo().then(() => console.log('b'))
Promise.resolve()
.then(() => console.log('c'))
.then(() => console.log('d'))
.then(() => console.log('e'))
// 输入后果 a c b d e
await
前面表达式如果是 Promise
,和一般值的后果是一样,毋庸期待then
时长。
为什么不和 return
为Promise
的情景一样是 2 次呢?原来这是 nodejs
在前期版本优化后的后果:移除了 2 个微工作,1 个throwaway promise
,具体起因能够查看「译」更快的 async 函数和 promises。
。
对于晚期版本(node 11
及以前),输入的后果是 c d a e b
,须要期待 2 个then
期待时长。
foo()
执行await
是一个Promise
,Promise
状态变更,此时期待 2 个then
时长;- 持续往下执行同步工作
Promise.resolve()
,遇到then
将console.log('c')
退出到微工作队列,以后同步工作执行实现; - 开始执行微工作队列,取出并执行
console.log('c')
,输入c
,微工作队列清空; - 遇到
then
将console.log('d')
入队,去除并执行,输入d
,微工作队列清空; - 此时步骤 1 期待时长到期,将
await
后续代码console.log('a')
入队; - 持续步骤 4,遇到
then
将console.log('e')
入队,而后顺次取出console.log('a')
和console.log('e')
并执行,输入a
和e
; - 执行完
console.log('a')
遇到then
将console.log('b')
放入队列,取出执行,输入b
;
综合 await
表达式值的后果,咱们能够总结
综合 async await
以上咱们仅仅从 async
的return
值和 await
表达式值繁多视角来看,上面综合他们两个来剖析(对立在 node 12+
环境)。
await 一个一般函数
首先,await 是一个一般函数(非 async
函数)
function baz() {// console.log('baz')
// return 'baz'
// return {// then(resolve) {// console.log('baz')
// resolve()
// }
// }
return new Promise((resolve) => {console.log('baz')
resolve()})
}
async function foo() {await baz()
console.log('a')
}
foo().then(() => console.log('b'))
Promise.resolve()
.then(() => console.log('c'))
.then(() => console.log('d'))
.then(() => console.log('e'))
// await baz 函数 return 是一般值 输入后果是 baz a c b d e
// await baz 函数 return 是 thenable 输入后果是 baz c a d b e
// await baz 函数 return 是 Promise 输入后果 baz a c b d e
与间接 await
表达式值输入统一。
- baz 函数
return
是一般值,不期待then
时长; - baz 函数
return
是thenable
,期待 1 个then
时长; -
baz 函数
return
是Promise
,不期待then
时长;await 一个 async 函数
而后将
baz
函数改成async
async function baz() {// console.log('baz') // return 'baz' // return {// then(resolve) {// console.log('baz') // resolve() // } // } return new Promise((resolve) => {console.log('baz') resolve()}) } async function foo() {await baz() console.log('a') } foo().then(() => console.log('b')) Promise.resolve() .then(() => console.log('c')) .then(() => console.log('d')) .then(() => console.log('e')) // await baz 函数 return 是一般值 输入后果是 baz a c b d e // await baz 函数 return 是 thenable 输入后果是 baz c a d b e // await baz 函数 return 是 Promise 输入后果 baz c d a e b // node12 以下版本 await baz 函数 return 是 Promise 输入后果 baz c d e a b
从中咱们能够发现:
await
`async函数的期待时长与
async baz函数的
return` 值期待时长保持一致。 - async baz 函数
return
是一般值,不期待then
时长; - async baz 函数
return
是thenable
,期待 1 个then
时长; - async baz 函数
return
是Promise
,期待 2 个then
时长,然而在node12
以下版本会期待 3 个then
时长;
综合 async、await、Promise、then 和 setTimeout
上面咱们综合 async
、await
、Promise
、then
和setTimeout
来看一道题目
const async1 = async () => {console.log('async1')
setTimeout(() => {console.log('timer1')
}, 2000)
await new Promise((resolve) => {console.log('promise1')
resolve()})
console.log('async1 end')
return Promise.resolve('async1 success')
}
console.log('script start')
async1().then((res) => console.log(res))
console.log('script end')
Promise.resolve(1)
.then(Promise.resolve(2))
.catch(3)
.then((res) => console.log(res))
setTimeout(() => {console.log('timer2')
}, 1000)
思考几分钟,输入后果
// script start
// async1
// promise1
// script end
// async1 end
// 1
// async1 success
// timer2
// timer1
- 执行同步工作输入
script start
和async1
,遇到setTimeout
放入宏工作队列; - 持续往下执行
await
表达式,执行new Promise
输入promise1
,Promise
状态变更,不期待then
时长,将后续代码增加到微工作队列; - 持续往下执行输入
script end
,执行Promise.resolve(1)
遇到then
将Promise.resolve(2)
放入微工作队列; - 再往下执行遇到
setTimeout
放入宏工作队列,至此同步工作执行结束; - 开始执行微工作队列,取出并执行步骤 2 的后续代码输入
async1 end
,返回一个已变更的Promise
对象,须要期待 2 个then
时长; - 持续取出微工作
Promise.resolve(2)
并执行,状态为resolved
前面走then
; - 遇到
then
将(res) => console.log(res)
放入微工作队列,而后取出并执行输入1
,留神:**then**
中是非函数表达式会执行,默认返回的是上一个**Promise**
的值 ,then(Promise.resolve(2))
会透传上一层的1
; - 此时步骤 5 期待时长到期,将
(res) => console.log(res)
放入微工作队列,而后取出并执行输入async1 success
; - 最初 2 个定时器别离到期,输入
timer2
和timer1
;
如果对这个案例再稍作革新
const async1 = async () => {console.log('async1')
setTimeout(() => {console.log('timer1')
}, 2000)
await new Promise((resolve) => {console.log('promise1')
})
console.log('async1 end')
return 'async1 success'
}
console.log('script start')
async1().then((res) => console.log(res))
console.log('script end')
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.catch(4)
.then((res) => console.log(res))
setTimeout(() => {console.log('timer2')
}, 1000)
// 输入后果:// script start
// async1
// promise1
// script end
// 1
// timer2
// timer1
具体过程就不一一列举了,从输入后果能够发现:如果 **await**
表达式的 **Promise**
的状态没有变更,以下代码以及前面的 **then**
永远都不会执行 。then
的执行机会是在后面函数执行实现并且 Promise
状态变更当前才会被增加到微工作队列中期待执行。
总结
通过以上就是根本 async
`await 的应用场景,以及综合
then、
Promise 和
setTimeout` 的混合应用,大抵能够总结如下几条法则:
async
函数的return
值为thenable
会期待 1 个then
时长,值为Promise
会期待 2 个时长;await
表达式值为thenable
会期待 1 个then
时长,值为Promise
在node12+
不期待then
时长,低版本node
期待 2 个then
时长;await
一个async
函数,async
函数的return
值为thenable
会期待 1 个then
时长,值为Promise
在node12+
会期待 2 个then
时长,在低版本node
期待 3 个then
时长;- 如果
then
中是非函数,表达式自身会执行,默认返回的是上一个Promise
的值,也就是透传上一个Promise
后果; - 如果
await
表达式的Promise
的状态没有变更,以下代码以及前面的then
永远都不会执行;
以上案例均通过试验运行得出,流程如有解释谬误,欢送斧正,完~