项目图片

SONA

weiping8/8/20265287

某某项目是面向全球中文开发者的技术内容分享与交流平台。我们通过技术文章、沸点、课程、直播等产品和服务,打造一个激发开发者创作灵感,激励开发者沉淀分享,陪伴开发者成长的综和服务

‌核心回答

JavaScript 的事件循环(Event Loop)是处理异步代码执行的核心机制。由于 JS 是‌单线程‌的,它通过事件循环实现非阻塞的异步行为,确保主线程不被耗时操作阻塞。以下是关键点:

  • 1. 核心组成

    • ‌调用栈(Call Stack):跟踪当前执行的函数,后进先出(LIFO)。
    • Web APIs‌:浏览器提供的异步 API(如 setTimeout, fetch),处理完任务后会将回调推入任务队列。
    • ‌任务队列(Task Queue):分为 ‌宏任务队列(MacroTask Queue)和 ‌微任务队列(MicroTask Queue)
      • 宏任务‌:setTimeoutsetInterval、I/O 操作、UI 渲染。
      • 微任务‌:Promise.thenMutationObserverqueueMicrotask

2. 事件循环的工作流程

  1. 执行同步代码‌:先处理调用栈中的同步代码。
  2. ‌处理异步任务‌:遇到异步操作(如 setTimeout),交给 Web APIs 处理,完成后将回调推入对应队列。
  3. ‌轮询队列‌:
    • 清空微任务队列‌:每轮事件循环结束前,必须执行完所有微任务。
    • 执行一个宏任务‌:从宏任务队列中取出一个任务执行。
  4. 重复循环‌:直到所有队列为空。
  • 3. 关键规则

    • 微任务优先级 > 宏任务‌:同一轮循环中,微任务会在宏任务之前执行。
    • 渲染时机‌:浏览器可能在宏任务之间进行 UI 渲染。
  • 示例代码分析

    • console.log("1"); // 同步代码
      
      setTimeout(() => console.log("2"), 0); // 宏任务
      
      Promise.resolve().then(() => {
        console.log("3"); // 微任务
      });
      
      console.log("4"); // 同步代码
      
      // 输出顺序为 1 → 4 → 3 → 2
      

更多详解

为什么 js 是单线程?

  • js 作为主要运行在浏览器的脚本语言,js 主要用途之一是操作 DOM。

  • 在 js 高程中举过一个栗子,如果 js 同时有两个线程,同时对同一个 dom 进行操作,这时浏览器应该听哪个线程的,如何判断优先级?

主线程

  • 主线程循环:即主线程会不停的从执行栈中读取事件,会执行完所有栈中的同步代码。

  • 当遇到一个异步事件后,并不会一直等待异步事件返回结果,而是会将这个事件挂在与执行栈不同的队列中,我们称之为任务队列(Task Queue)。

js 异步执行的运行机制。

  1. 所有任务都在主线程上执行,形成一个执行栈。
  2. 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
  3. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列"。那些对应的异步任务,结束等待状态,进入执行栈并开始执行。
  4. 主线程不断重复上面的第三步。

宏任务与微任务

异步任务分为 宏任务(macrotask) 与 微任务 (microtask),不同的 API 注册的任务会依次进入自身对应的队列中,然后等待 Event Loop 将它们依次压入执行栈中执行。

  • 宏任务(macrotask) => script(整体代码)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)
  • 微任务(microtask) => Promise、 MutaionObserver、process.nextTick(Node.js 环境)

Event Loop(事件循环)

Event Loop(事件循环)中,每一次循环称为 tick, 每一次tick的任务如下:

  • 执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行
  • 检查是否存在 Microtask,如果存在则不停的执行,直至清空 microtask 队列
  • 更新render(每一次事件循环,浏览器都可能会去更新渲染)
  • 重复以上步骤

宏任务 > 所有微任务 > 宏任务,如下图所示:

  • js事件循环