JavaScript中的事件循环机制
字数 1112 2025-11-02 08:11:07
JavaScript中的事件循环机制
描述
事件循环是JavaScript实现异步编程的核心机制。由于JavaScript是单线程语言,它通过事件循环模型来处理异步操作(如定时器、网络请求等),避免阻塞主线程。理解事件循环需要掌握调用栈、任务队列和微任务队列等概念。
解题过程
-
基本概念
- 调用栈:用于跟踪当前正在执行的函数。当函数被调用时,它会被推入栈顶;执行完毕后从栈中弹出。
- Web APIs:浏览器提供的异步功能(如
setTimeout、fetch),JavaScript会将这些操作交给浏览器后台处理,完成后将回调函数放入任务队列。 - 任务队列(宏任务队列):存放异步操作的回调函数(例如
setTimeout、DOM事件的回调)。 - 微任务队列:存放需要尽快执行的回调(例如
Promise.then、MutationObserver)。
-
事件循环的工作流程
- 步骤1:执行调用栈中的同步代码(优先级最高),遇到异步操作时交给Web APIs处理。
- 步骤2:同步代码执行完毕后,事件循环先检查微任务队列,依次执行所有微任务(直到队列清空)。
- 步骤3:微任务执行完后,必要时渲染页面(浏览器自行决定时机)。
- 步骤4:从任务队列中取出一个宏任务执行(如
setTimeout回调),然后回到步骤2检查微任务队列。
-
示例分析
console.log("Start"); setTimeout(() => console.log("Timeout"), 0); Promise.resolve().then(() => console.log("Promise")); console.log("End");- 执行过程:
- 同步代码:输出"Start" → 将
setTimeout回调交给Web APIs(0毫秒后放入任务队列)→ 将Promise.then回调放入微任务队列 → 输出"End"。 - 同步代码结束,检查微任务队列:输出"Promise"。
- 微任务清空后,从任务队列取出
setTimeout回调执行:输出"Timeout"。
- 同步代码:输出"Start" → 将
- 执行过程:
-
关键规则
- 微任务优先级高于宏任务(例如
Promise.then会在setTimeout前执行)。 - 同类型任务按顺序执行,但微任务队列必须完全清空后才会处理宏任务。
- 微任务优先级高于宏任务(例如
-
复杂场景
setTimeout(() => console.log("Timeout 1"), 0); Promise.resolve().then(() => { console.log("Promise 1"); setTimeout(() => console.log("Timeout 2"), 0); }); Promise.resolve().then(() => console.log("Promise 2"));- 输出顺序:
- 同步代码执行后,微任务队列包含两个
Promise.then回调。 - 按顺序输出"Promise 1" → "Promise 2"(微任务队列清空)。
- 执行宏任务:先输出"Timeout 1",然后执行第二个
setTimeout(在微任务中注册)输出"Timeout 2"。
- 同步代码执行后,微任务队列包含两个
- 输出顺序:
-
常见陷阱
- 在微任务中递归添加微任务会阻塞主线程(因为事件循环会持续清空微任务队列)。
- 宏任务(如
setTimeout)的延迟时间是最短等待时间,不保证精确执行。
通过理解这些步骤,可以预测异步代码的执行顺序,避免常见的并发问题。