JavaScript事件循环工作原理详解 | 微任务与宏任务执行顺序解析

大家好,我是第八哥,作为一名有10年经验的前端开发者,我发现很多同学在面试或者写异步代码时,常常被JavaScript事件循环(Event Loop) 搞得一头雾水。本文将用简单易懂的方式,带你彻底掌握事件循环的原理、执行顺序、常见陷阱和实战技巧。

一、为什么要了解事件循环?

JavaScript是单线程的,这意味着同一时间只能处理一个任务。但Web应用越来越复杂,如果同步执行,页面很容易卡死。为了解决这个问题,JS引入了事件循环机制来管理异步任务。

只要你写过setTimeoutPromiseasync/await,甚至操作DOM,你就已经在用事件循环了。

二、事件循环核心概念

事件循环主要处理两类任务:宏任务(Macro Task)微任务(Micro Task)

1. 宏任务(Macro Task)

包括:setTimeoutsetIntervalsetImmediateI/O、UI渲染等。

2. 微任务(Micro Task)

包括:Promise.thenMutationObserverqueueMicrotask等。

3. 执行顺序

核心规则:每次宏任务执行完毕后,立即清空所有微任务,然后再执行下一个宏任务。

三、经典示例

console.log('start');

setTimeout
(() => console.log('setTimeout'), 0);
Promise
.resolve()
    .then(() => console.log('promise1'))
    .then(() => console.log('promise2'));
    
console
.log('end');

输出结果:

start
end
promise1
promise2
setTimeout

原因:

  1. 1. 同步任务先执行startend
  2. 2. 遇到Promise,回调放入微任务队列。
  3. 3. 遇到setTimeout,回调放入宏任务队列。
  4. 4. 同步执行结束后,立即执行所有微任务。
  5. 5. 最后进入下一个宏任务,执行setTimeout

四、async/await与事件循环

async function test() {
    console
.log('1');
    await
 Promise.resolve();
    console
.log('2');
}
test
();
console
.log('3');

输出结果:

1
3
2

原理:
await会让后续代码放入微任务队列,因此console.log('3')会先执行。

五、Node.js中的事件循环

在Node.js中,事件循环稍微复杂一些,分为以下几个阶段:

  1. 1. timers(处理setTimeout/setInterval
  2. 2. I/O callbacks
  3. 3. idle, prepare
  4. 4. poll(轮询I/O事件)
  5. 5. check(setImmediate回调)
  6. 6. close callbacks

Node.js会在每个阶段结束后检查微任务队列,并立即执行所有微任务。

六、常见坑点与实战技巧

1. setTimeout vs setImmediate

在Node.js中,setImmediate会比setTimeout(fn, 0)更快执行,因为它在当前事件循环的check阶段触发。

2. 使用queueMicrotask

如果希望在当前宏任务结束后立刻执行某些逻辑,可以用:

queueMicrotask(() => console.log('microtask'));

3. 避免微任务过多

在Promise链中堆叠过多的微任务,可能会阻塞渲染和后续宏任务,导致页面卡顿。

七、最佳实践

  • • 熟悉宏任务和微任务的区别。
  • • 合理使用Promiseasync/await
  • • 在性能敏感场景中,避免Promise链过长。
  • • 调试复杂异步逻辑时,可用console.log标记执行顺序。

八、总结

事件循环是JavaScript异步编程的核心。理解宏任务、微任务和执行顺序,可以帮你写出更高性能、更可控的代码。无论是浏览器还是Node.js,掌握事件循环原理都是进阶前端开发的必修课。

上一篇 从零开始掌握JavaScript的Map和Set | 高效数据结构与实战技巧 下一篇 .NET Core定时任务实战指南 | 多种实现方式与性能优化技巧

评论

暂不支持评论