请求解析JS代码在调用栈与事件循环中的执行流程
基于调用栈与事件循环的JavaScript代码执行流程解析及验证方法
完整执行流程(结合调用栈与事件循环)
以下是对应代码的完整执行步骤(基于你梳理的前7步延续):
- 全局执行上下文(GEC)推入调用栈,启动全局代码执行。
- 调用
stuff(),stuff()执行上下文入栈,置于GEC上方。
- 调用
- 执行
console.log('Stuff start'):语句入栈执行,输出Stuff start,执行完成后弹出调用栈。
- 执行
- 调用
fetch(),fetch()执行上下文入栈,置于stuff()上方。
- 调用
- 执行
console.log('Fetch start'):语句入栈执行,输出Fetch start,执行完成后弹出调用栈。
- 执行
- 调用
new Promise():构造函数入栈,此时调用栈为GEC => stuff() => fetch() => new Promise()。Promise的executor回调同步立即执行,executor函数上下文入栈,调用栈变为GEC => stuff() => fetch() => new Promise() => executor。
- 调用
- 执行
console.log('Executor'):语句入栈执行,输出Executor,执行完成后弹出调用栈。
- 执行
- 执行
setTimeout():函数入栈执行,将回调发送至Web APIs并设置1秒定时器,执行完成后弹出调用栈;随后executor执行完毕,弹出调用栈。
- 执行
new Promise()执行完成,返回Promise实例,调用.then()方法:该方法会将回调注册为Promise的微任务,存入微任务队列,.then()执行完成后弹出调用栈。
fetch()执行完毕弹出调用栈,stuff()执行完毕弹出调用栈,此时调用栈仅剩GEC。
- 调用栈清空后,事件循环优先检查微任务队列:此时Promise未resolve,
.then()回调未触发,微任务队列为空,跳过处理。
- 调用栈清空后,事件循环优先检查微任务队列:此时Promise未resolve,
- 执行全局代码剩余的
console.log('Sync log'):语句入栈执行,输出Sync log,执行完成后弹出调用栈;GEC执行完毕弹出调用栈,调用栈完全清空。
- 执行全局代码剩余的
- 1秒后,Web APIs定时器到期,将
setTimeout回调推入宏任务队列。
- 1秒后,Web APIs定时器到期,将
- 事件循环检测到调用栈为空,处理宏任务队列:将
setTimeout回调入栈执行。
- 事件循环检测到调用栈为空,处理宏任务队列:将
- 执行
resolve('Promise resolved'):触发Promise状态变为fulfilled,同时将.then()回调推入微任务队列。
- 执行
setTimeout回调执行完毕弹出调用栈,调用栈再次清空。
- 事件循环立即处理微任务队列:将
.then()回调入栈执行。
- 事件循环立即处理微任务队列:将
- 执行
console.log(val):输出Promise resolved,执行完成后弹出调用栈,微任务队列清空。
- 执行
- 事件循环持续检测任务队列,无新任务则终止。
Chrome DevTools验证方法
- 打开Chrome浏览器按
F12启动DevTools,切换到Sources面板。 - 在代码的关键行(比如各类
console.log、setTimeout回调、.then()回调)点击行号左侧添加断点。 - 点击顶部的「Resume script execution」按钮(▶️)逐步执行代码,实时查看右侧Call Stack面板的调用栈变化,确认执行上下文的入栈/出栈顺序。
- 切换到Console面板,查看代码输出的顺序是否符合流程预期。
- 若要可视化任务执行时间线,可打开Performance面板,点击录制按钮(🔴)后触发代码执行,录制完成后可查看宏任务、微任务的执行时序。
- 在代码中插入
console.trace()语句,可在执行时打印当前调用栈,快速验证栈结构。
内容的提问来源于stack exchange,提问作者kekerinho
相关产品推荐
相关产品推荐

