JavaScript 事件循环详解

JavaScript 事件循环详解

1. 单线程与异步的必要性

JavaScript 是单线程语言,意味着它一次只能执行一个任务。为了避免耗时操作(如网络请求、定时器)阻塞主线程,JavaScript 使用事件循环机制处理异步任务,确保非阻塞执行。

2. 事件循环的核心组成
  • 调用栈(Call Stack):主要针对函数,如果函数里面调用函数,遵循后进先出(LIFO)原则。
  • 任务队列(Task Queue/Macrotask Queue):存放宏任务(如 setTimeoutI/O 操作)。
  • 微任务队列(Microtask Queue):存放微任务(如 Promise.thenMutationObserver)。
  • Web APIs:浏览器提供的线程(如定时器线程、DOM 事件线程),处理异步操作后将回调推入队列。
3. 宏任务(Macrotasks)与微任务(Microtasks)
  • 宏任务:包括 setTimeoutsetIntervalI/O、UI 渲染、requestAnimationFrame(部分浏览器)。
  • 微任务:包括 Promise.thenMutationObserverprocess.nextTick(Node.js)。
4. 事件循环流程
  1. 执行同步代码:直至调用栈清空。
  2. 处理微任务队列:依次执行所有微任务(包括执行过程中新生成的微任务)。
  3. 渲染页面(如需要):执行 requestAnimationFrame 回调,更新 UI。
  4. 取一个宏任务执行:从宏任务队列中取出并执行,重复整个循环。
5. 执行顺序示例

示例 1:基础顺序

console.log('Start');

setTimeout(() => console.log('Timeout'), 0);

Promise.resolve().then(() => console.log('Promise'));

console.log('End');

输出

Start → End → Promise → Timeout

解析

  • 同步代码先执行,输出 StartEnd
  • 微任务(Promise)优先于宏任务(Timeout)执行。

示例 2:嵌套任务

setTimeout(() => {
  console.log('Timeout 1');
  Promise.resolve().then(() => console.log('Promise inside Timeout'));
}, 0);

setTimeout(() => {
  console.log('Timeout 2');
  Promise.resolve().then(() => console.log('Promise inside Timeout 2'));
}, 0);

输出

Timeout 1 → Promise inside Timeout → Timeout 2 → Promise inside Timeout 2

解析

  • 每个宏任务执行后立即处理其产生的微任务。

示例 3:微任务递归

function loopMicrotasks() {
  Promise.resolve().then(loopMicrotasks);
}
loopMicrotasks();
// 主线程被阻塞,无法执行后续任务。

解析:微任务队列永不空,导致无限循环,阻塞后续任务。

6. 与渲染相关的行为

requestAnimationFrame 在渲染前执行,属于宏任务,但优先级高于普通宏任务:

setTimeout(() => console.log('Timeout'), 0);
requestAnimationFrame(() => console.log('RAF'));
// 可能输出:RAF → Timeout(取决于浏览器渲染时机)
7. Node.js 与浏览器差异
  • Node.js 事件循环:分阶段(Timers、Poll、Check 等),setImmediateprocess.nextTick 优先级不同。
  • 浏览器:更简化的宏任务/微任务模型,requestAnimationFrame 在渲染前触发。
8. 关键点总结
  • 微任务优先:每个宏任务后必须清空微任务队列。
  • 避免阻塞:长时间运行的微任务会阻塞渲染。
  • API 分类:正确区分宏任务和微任务来源(如 Promise 为微任务,setTimeout 为宏任务)。
9. 综合示例
console.log('Script start');

setTimeout(() => {
  console.log('Timeout 1');
  Promise.resolve().then(() => console.log('Promise 1'));
}, 0);

Promise.resolve().then(() => {
  console.log('Promise 2');
  setTimeout(() => console.log('Timeout 2'), 0);
});

console.log('Script end');

输出

Script start → Script end → Promise 2 → Timeout 1 → Promise 1 → Timeout 2

解析

  1. 同步代码执行。
  2. 微任务 Promise 2 触发,添加宏任务 Timeout 2
  3. 宏任务 Timeout 1 执行,触发微任务 Promise 1
  4. 所有微任务处理完毕后,执行下一个宏任务 Timeout 2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值