跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

如何理解JavaScript中的微任务与宏任务_JavaScript任务队列如何影响代码执行

JavaScript执行是单线程的,靠事件循环调度微任务(如Promise.then、queueMicrotask)和宏任务(如setTimeout、I/O),微任务在同步代码后立即清空执行,宏任务每次只执行一个且中间插入微任务检查。 JavaScript 的执行是单线程的,但靠任务队列机制实现异步行为。微任务(microtask)和宏任务( mac rotask)不是语法层面的概念,而是事件循环(Event Loop)中对任务分类调度的实际规则——理解它们的关键,在于知道哪些操作会进入哪类队列、谁先执行、以及为什么
Promise.then
总比
setTimeout
先触发。 哪些操作产生微任务? 微任务在当前同步代码执行完后、下一轮宏任务开始前立即执行,且会清空整个微任务队列(即连续执行,不穿插渲染或 I/O)。常见来源包括:
Promise.prototype.then
/
.catch
/
.finally
回调(即使
Promise.resolve().then(...)
)
queueMicrotask()
显式加入的回调 MutationObserver 的回调
await
后续代码(本质是 Promise 微任务链的一部分) 注意:
Promise
构造函数内的执行器(executor)是同步运行的,只有其内部的
resolve
/
reject
调用才会触发后续微任务。 哪些操作产生宏任务? 宏任务每次只执行一个,执行完后检查并清空全部微任务,再取下一个宏任务。典型来源有: 立即学习 “ Java免费学习笔记(深入) ”;
setTimeout
/
setInterval
setImmediate
(Node. js 独有,非标准) I/O 回调(如
fs.readFile
在 Node.js 中) UI 渲染( 浏览器 中,虽不可直接调度,但属于宏任务周期的一部分)
postMessage
和
MessageChannel
的消息处理 特别注意:
setTimeout(fn, 0)
并不意味着“立刻执行”,它只是尽快把
fn
推入宏任务队列——实际执行要等当前宏任务 + 所有微任务完成之后。 Comet AI 浏览器 下载 Comet AI 浏览器,体验由 Perplexity AI 驱动的革命性上网方式。内置 AI 助手可实时总结网页、跨标签页对比信息、自动执行任务。告别繁琐操作,让 AI 成为你的浏览副驾,大幅提升研究与工作效率。支持 Windows、macOS、Android 和 iOS。 下载 执行顺序如何确定?看一个典型例子 下面这段代码能清晰体现两者的嵌套关系:
console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); Promise.resolve().then(() => { console.log(4); setTimeout(() => console.log(5), 0); }); console.log(6);
输出顺序是:
1
→
6
→
3
→
4
→
2
→
5
。原因如下: 同步代码先执行:
1
和
6
然后清空微任务队列:两个
Promise.then
(
3
和
4
),其中第二个还触发了一个新的
setTimeout
(推入宏任务队列) 接着取下一个宏任务:
setTimeout
的
2
最后执行新推入的宏任务:
5
关键点:微任务可以递归产生新微任务(比如
then
里再返回
Promise
),它们都会被追加到当前微任务队列末尾,并在本轮全部执行完;而宏任务之间永远是串行的,中间必然插入微任务检查。 容易忽略的坑:async/await 与错误捕获
async
函数返回的是 Promise,
await
后面的表达式一旦 resolve/reject,后续代码会被包装进微任务。这意味着: 未被
try/catch
包裹的
await
错误,不会同步抛出,而是变成 rejected Promise,需靠
.catch
或顶层
unhandledrejection
捕获 多个
await
连续写,它们之间是微任务衔接,不是同步栈;调试时断点可能“跳过”中间状态
await Promise.resolve()
会触发一次微任务调度,哪怕值已确定——这在性能敏感循环中可能成为隐性开销 真正难调试的,往往不是“谁先谁后”,而是微任务队列被意外延长(比如某个
then
里又创建了新 Promise 链),导致预期中的 DOM 更新延迟或状态不同步。

相关文章