Skip to content

事件循环机制

事件循环 EventLoop

为什么会有事件循环

由于 JavaScript 是单线程语言(即程序运行时只有一个线程存在,同一时间只能做一件事),单线程会有线程运行阻塞问题(同步代码执行时,会阻塞后续代码的执行),为了解决这一问题,JavaScript 便采用了事件循环 EventLoop 机制。

为什么不设计成多线程

既然单线程有运行阻塞问题,为什么 JavaScript 不设计成多线程呢?

设计之初 JavaScript 作为一门浏览器脚本语言,通常用于操作 DOM,若设计成多线程,则会带来很复杂的同步问题,比如:一个线程要删除某 DOM 元素,另一个要添加这个 DOM 元素,此时的浏览器就不知道该怎么办了!

什么是事件循环

事件循环是一种机制,它允许 JavaScript 在单线程环境中实现异步编程。事件循环不断地从任务队列中取出任务并执行,直到任务队列清空。

  • 同步代码:指按照代码的书写顺序一步一步执行的代码,会阻塞后续代码。
  • 异步任务:不进入主线程,进入事件队列的任务(某个异步任务可以执行了,该任务才会进入主线程执行)。异步任务又分为微任务和宏任务:
    • 微任务(Microtask):Promiseprocess.nextTickMutationObserver、异步函数(async/await)
    • 宏任务(Macrotask):setTimeoutsetIntervalsetImmediaterequestAnimationFrameI/OUI rendering

执行顺序:同步代码 > 微任务 > 宏任务

事件循环的工作流程:

  1. 执行同步代码,这些代码会立即进入调用栈并执行。
  2. 当调用栈为空时,检查微任务队列。如果有微任务,则依次执行所有微任务。
  3. 微任务队列清空后,从宏任务队列中取出一个任务执行。
  4. 重复步骤 2 和 3,直到所有任务都执行完毕。

示例

js
async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}

async function async2() {
  console.log('async2')
}

console.log('script start')

setTimeout(() => {
  console.log('setTimeout')
}, 0)

async1()

new Promise((resolve) => {
  console.log('Promise1')
  resolve()
}).then(() => {
  console.log('Promise2')
})

console.log('script end')

这段代码的输出顺序如下:

  1. "script start"
  2. "async1 start"
  3. "async2"
  4. "Promise1"
  5. "script end"
  6. "async1 end"
  7. "Promise2"
  8. "setTimeout"
Preview

解释一下执行过程:

  1. 首先执行同步代码,输出 "script start"。

  2. 遇到 setTimeout,将其回调函数放入宏任务队列。

  3. 调用 async1()

    • 输出 "async1 start"
    • 调用 async2(),输出 "async2"
    • await 后的代码被放入微任务队列
  4. 执行 new Promise

    • 输出 "Promise1"
    • resolve() 被调用,.then 的回调被放入微任务队列
  5. 输出 "script end",同步代码执行完毕。

  6. 开始执行微任务队列:

    • 首先执行 async1await 后的代码,输出 "async1 end"
    • 然后执行 Promise 的 .then,输出 "Promise2"
  7. 微任务队列清空后,执行宏任务队列中的 setTimeout 回调,输出 "setTimeout"

这个输出顺序体现了 JavaScript 的事件循环机制,包括同步代码执行、微任务和宏任务的处理顺序。异步函数(async/await)和 Promise 的处理属于微任务,而 setTimeout 属于宏任务。微任务总是在当前任务执行结束后、下一个宏任务开始前执行。

参考

JavaScript 运行机制详解:再谈Event Loop

微任务、宏任务与Event-Loop