事件循环 (Event Loop)

1. 事件循环 (Event Loop)

事件循环是 JavaScript 处理异步操作的核心机制。它允许单线程的 JavaScript 执行非阻塞的 I/O 操作。

宏任务 vs. 微任务

  • 宏任务: 由浏览器/Node.js 环境发起的任务。例如:

    • setTimeoutsetInterval

    • setImmediate (Node.js 特有)

    • I/O 操作 (如读取文件、网络请求)

    • UI 渲染 (浏览器)

    • 整体的 script 代码本身

  • 微任务: 由 JavaScript 自身发起的任务,通常与“承诺”相关。例如:

    • Promise.then()Promise.catch()Promise.finally()

    • process.nextTick (Node.js 特有,优先级最高)

    • MutationObserver (浏览器)

执行规则 (浏览器)

  1. 执行一个宏任务(通常是最老的或最先入队的),例如执行全局的脚本。

  2. 执行过程中遇到微任务,就将其放入微任务队列;遇到宏任务,就将其放入宏任务队列。

  3. 当前宏任务执行完毕后,立即清空整个微任务队列(执行所有微任务)。

  4. 进行 UI 渲染(如果需要)。

  5. 从宏任务队列中取出下一个宏任务,开始新一轮的循环。

核心:一个宏任务 → 所有微任务 → (渲染) → 下一个宏任务。

Node.js 与浏览器的差异

Node.js 的事件循环基于 libuv 库,其阶段划分比浏览器更复杂。

Node.js 事件循环阶段 (简化版):

  1. Timers: 执行 setTimeout 和 setInterval 的回调。

  2. Pending callbacks: 执行延迟到下一个循环的 I/O 回调。

  3. Idle, Prepare: 内部使用。

  4. Poll: 检索新的 I/O 事件;执行 I/O 相关的回调(如读取文件)。必要时会在此阶段阻塞

  5. Check: 执行 setImmediate 的回调。

  6. Close callbacks: 执行关闭事件的回调(如 socket.on('close', ...))。

process.nextTick() 在一个阶段结束后、下一个阶段开始前执行,它拥有一个独立的队列,优先级高于微任务

主要差异总结:

特性 浏览器 Node.js
阶段 相对简单(宏任务、微任务、渲染) 分为多个明确阶段(Timers, Poll, Check等)
setImmediate 不支持 支持,在 Check 阶段执行
process.nextTick 不支持 支持,优先级最高(在各阶段间执行)
微任务执行时机 在每个宏任务之后 在事件循环的每个阶段之后

复杂异步代码执行顺序示例

console.log('1. Script Start'); // 同步代码,宏任务1开始
let a;
let b = new Promise(resolve=>{
  console.log(12)
  setTimeout(()=>{
    resolve();
  }, 1000)
})
a= new Promise(resolve=>{
  console.log(a)
  b.then(()=>{
    console.log(a)
    console.log(13)

    a.then(()=>{ // 没有resolve,后面不执行
      resolve(true) 
      console.log(14)
    })
  })
})
Promise.resolve()
  .then(() => {
    console.log('7. Promise 11'); // 微任务
    return Promise.resolve(10)   // return Promise.resolve().then(() => {return 10 }) 相当于增加了两次空的微任务
  }).then((a) => {
    console.log(a + '. Promise 12'); // 微任务
  })
  .then(() => {
    console.log('8. Promise 21'); // 微任务
  });

setTimeout(() => {
  console.log('2. setTimeout'); // 回调是宏任务
  Promise.resolve().then(() => {
    console.log('3. Promise inside setTimeout'); // 微任务
  });
}, 0);

Promise.resolve()
  .then(() => {
    console.log('4. Promise 1'); // 微任务
  })
  .then(() => {
    console.log('5. Promise 2'); // 微任务
  })
 .then(() => {
    console.log('9. Promise 3'); // 微任务
  })
 .then(() => {
    console.log('10. Promise 4'); // 微任务
  });

console.log('6. Script End'); // 同步代码,宏任务1结束

// 预期输出顺序:
// 1. Script Start
// 12
// undefined
// 6. Script End
// 7. Promise 11
// 4. Promise 1
// 5. Promise 2
// 9. Promise 3
// 10. Promise 12
// 10. Promise 4
// 8. Promise 21
// 
// 2. setTimeout
// 3. Promise inside setTimeout
// Promise{<pending>}
// 13
posted @ 2025-10-10 13:35  阿木隆1237  阅读(13)  评论(0)    收藏  举报