为何async函数内的setTimeout用await仍不等待?求解惑
为什么await setTimeout不生效?问题解析与解决办法
核心问题:async函数的Promise不会自动绑定内部异步任务
你说的没错,async函数确实会隐式返回Promise,但这个Promise的状态不会自动跟随内部的setTimeout这类异步任务变化。
async函数的执行逻辑是:
- 同步执行函数内代码,直到遇到
await或者函数执行完毕 - 如果执行到末尾都没遇到需要等待的pending Promise,它会立刻返回一个resolved状态的Promise
而setTimeout只是把回调扔进事件队列,本身不返回Promise,也不会阻止async函数执行到末尾。所以你的f函数调用后,会立刻返回resolved的Promise,await自然不会等setTimeout的回调执行。
你的两个示例为什么不符合预期?
示例1的实际运行逻辑
const f = async(x) => { setTimeout(() => { console.log(x, 'done!!!') }, x) } (async() => { await f(2000) // f立刻返回resolved Promise,await直接跳过 await f(1000) // 同上 await f(500) // 同上 console.log('all done!') // 立刻执行 })()
实际输出顺序:
all done!
500 done!!!
1000 done!!!
2000 done!!!
三个await f()都立刻完成,all done!先打印,之后三个setTimeout的回调按延时长短依次触发。
示例2的实际运行逻辑
const f = async(x) => { setTimeout(() => { console.log(x, 'done!!!') }, x) } (async() => { await (async() => { f(2000) // 调用f但没await,f立刻执行完返回Promise f(1000) // 同上 f(500) // 同上 })() // 内部async函数没遇到await,立刻返回resolved Promise console.log('all done!') // 立刻执行 })()
实际输出和示例1完全一致,因为内部的async函数也没等待任何异步任务,await直接跳过。
正确的处理方式:把setTimeout包装成Promise
要让await能等待setTimeout,必须手动把它包装成Promise,在setTimeout的回调里触发Promise的resolve,告诉JS“这个异步任务完成了”。
改造函数f,实现示例1的预期
// 不用async也可以,直接返回Promise就行 const f = (x) => { return new Promise(resolve => { setTimeout(() => { console.log(x, 'done!!!') resolve() // 触发Promise的resolve,通知await可以继续 }, x) }) } (async() => { await f(2000) // 等待2秒,打印后继续 await f(1000) // 等待1秒,打印后继续 await f(500) // 等待0.5秒,打印后继续 console.log('all done!') })()
输出符合预期:
2000 done!!!
1000 done!!!
500 done!!!
all done!
改造实现示例2的预期
如果想让三个setTimeout并行执行,等所有回调跑完再打印all done!,用Promise.all:
const f = (x) => { return new Promise(resolve => { setTimeout(() => { console.log(x, 'done!!!') resolve() }, x) }) } (async() => { // Promise.all会等待所有传入的Promise都resolve后才继续 await Promise.all([f(2000), f(1000), f(500)]) console.log('all done!') })()
输出符合预期:
500 done!!!
1000 done!!!
2000 done!!!
all done!
总结
await只能等待pending状态的Promise,遇到resolved状态的Promise会直接跳过- async函数返回的Promise,只有当内部遇到
await某个pending Promise,或者手动返回pending Promise时,才会处于pending状态 - 像setTimeout、AJAX这类传统异步API,必须手动包装成Promise,才能被
await正确等待
内容的提问来源于stack exchange,提问作者hoo
相关产品推荐
相关产品推荐

