JS中async函数执行上下文工作原理:为何End先于Hello输出?
问题背景
我正在学习JavaScript,遇到了一段JS代码片段,无法理解其运行逻辑。
代码如下:
let hello = () => Promise.resolve("Hello") async function fun() { console.log("Inside") const result = await hello() console.log(result) } console.log("Start") fun() console.log("End")
控制台输出结果为:
Start Inside End Hello
我不理解为何使用await关键字后,End仍会在Hello之前打印,希望了解该代码片段在事件循环中的运行机制,尤其是当fun()被推入调用栈时的具体过程。
执行过程详细拆解
JavaScript是单线程语言,同步任务在调用栈按顺序执行,异步任务会被放入任务队列,等调用栈清空后才会处理。这里的核心是await的工作逻辑,一步步拆解:
第一步:执行最外层同步代码
调用栈先执行console.log("Start"),控制台输出Start,执行完毕后调用栈清空。第二步:调用
fun()并执行内部同步代码fun()被推入调用栈,先执行里面的同步语句console.log("Inside"),控制台输出Inside。
接下来遇到await hello():- 先执行
hello(),返回一个已完成状态的Promise。 await会暂停fun()的执行,把fun()从调用栈弹出,同时将await后面的代码(console.log(result))打包成回调函数,放到微任务队列里等待执行。
- 先执行
第三步:继续执行剩余同步代码
调用栈现在是空的,主线程继续执行最外层剩下的同步代码console.log("End"),控制台输出End,所有同步任务执行完毕,调用栈再次清空。第四步:处理微任务队列
调用栈清空后,事件循环会优先处理微任务队列。找到之前await生成的回调函数,将其推入调用栈执行:
回调函数拿到Promise的返回值"Hello",赋值给result,然后执行console.log(result),控制台输出Hello。
核心结论
await并不会阻塞整个主线程,它只是暂停当前async函数的执行,让主线程先把所有同步代码执行完毕,之后再回来处理await后续的逻辑。而Promise的回调属于微任务,会在所有同步任务完成后、下一轮事件循环启动前被处理,这就是为什么End会比Hello先打印。
内容的提问来源于stack exchange,提问作者Arghya Saha

