返回首页

事件循环机制

布莱克2026-05-13 22:17已编辑
Tip:文章封面与内容无关,作者旅游时拍摄,因为没什么值得把四季都错过!

JS的运行机制

JavaScript 是单线程的,这意味着它一次只能做一件事。为了不让耗时任务(如网络请求)阻塞主线程,它引入了非阻塞的事件循环机制。

js的任务分为同步任务和异步任务

同步任务在主线程上排队执行,前一个任务执行完毕再执行下一个任务,

######

JavaScript 本身是一段脚本,不能独立运行,必须依靠 JavaScript 引擎(如 Chrome 的 V8)。

  • 浏览器环境: JS 运行在浏览器中,目的是为了操作 DOM、处理用户点击、发起网络请求。它通过浏览器提供的 Web APIs(如 window, document)与外界交互。
  • Node.js 环境: JS 运行在你的操作系统上。它是基于 V8 引擎的一个运行时(Runtime)。它去掉了浏览器的 DOM/BOM,但增加了操作系统级别的能力,比如读写文件 (fs)、创建网络服务器 (http)、操作进程 (process)

运行过程(浏览器端)

事件循环的每一轮(Tick)都严格遵循以下顺序:

  1. 执行同步代码: 在等待异步任务准备的同时,js引擎去执行其他同步任务
  2. 清空微任务 (Microtasks): 执行栈为空后,立即清空当前微任务队列中所有的任务。如果在执行微任务期间又产生了新的微任务,会直接加入到当前队列尾部,并在本轮循环中执行完。
  3. 执行宏任务 (Macrotasks): 从宏任务队列中取出一个(且仅一个)任务执行。
  4. 循环: 回到步骤 2。

宏任务 微任务

宏任务是由宿主环境(浏览器或 Node.js)发起的任务。它们通常代表一些离散的、独立的工作单元

微任务是由 JavaScript 引擎本身发起的任务。它们旨在当前任务执行完后,立即处理一些“需要尽快收尾”的工作

常见宏任务:

setTimeout / setInterval定时逻辑:用于非精确的延时操作,或轮询任务。

setImmediate (Node.js特有)插队逻辑:在当前 Poll 阶段结束后立即执行,效率通常高于 setTimeout(0)。

ajax,DOM操作

I/O 操作文件/网络:读取文件、数据库操作、网络请求回调。

UI 渲染 (浏览器)视觉更新:浏览器重新绘制页面,确保用户看到最新的界面。


常见微任务:

Promise.then/catch/finally异步流控制:处理异步操作的结果,确保逻辑的连续性。

process.nextTick (Node.js)绝对优先:在当前同步代码执行完后“立即”执行,甚至在 Promise 之前。

MutationObserverDOM 监听:当 DOM 发生变化时异步通知,避免频繁同步操作导致性能问题。


思考🤔

当一个页面完全静止、没有任何 JS 运行、没有任何用户操作时,这个“事件循环”还在跑吗?

它在“逻辑上”依然存在,但在“物理上”它进入了休眠状态。

如果事件循环是一个死守岗位的“士兵”,当没有任何任务时,他不会疯狂地原地打转(那样会白白消耗 100% 的 CPU),而是会进入一种“待命挂起”状态。

1. 这种状态叫什么?(Event Loop 的休眠)

当渲染主线程的任务队列(Task Queue)为空时:

  1. 主线程会向操作系统发出一个系统调用:“我没活干了,请让我睡觉。如果队列里有了新任务,或者用户动了鼠标,请立刻叫醒我。”
  2. 操作系统接管控制权,将该线程置为“等待/休眠”状态。
  3. 此时,CPU 占用率几乎为 0。


事件循环机制类问题:

async function async1() {
  console.log('1');
  await async2();
  console.log('2');
}

async function async2() {
  console.log('3');
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('4');
      resolve();
    }, 0);
  });
}

console.log('5');

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

async1();

new Promise((resolve) => {
  console.log('8');
  resolve();
}).then(() => {
  console.log('9');
});

console.log('10');

结果:

5 1 3 10 8 9 6 7 4 2

浏览器

浏览器是一个多进程的架构设计,在浏览器中打开一个网页相当于开启一个新进程

## 主要包含以下几种进程

    主进程:(负责协调 主控,管理用户界面 管理子进程)

    第三方插件进程:负责在后台运行浏览器插件 有多个

    GPU进程:(用于3D绘制,视频解码等)

    浏览器渲染进程:(负责页面渲染,脚本执行,事件处理等,通常有多个),当满足某些条件时会做优化,比如相同tab页面共用一个渲染进程

    网络进程:负责处理所有的网络请求,包括http请求


## 多线程的浏览器渲染进程

   GUI渲染线程:负责渲染浏览器界面(解析HTML,CSS,构建DOM树和CSSOM树和render树)

   JS引擎线程:负责处理js脚本程序(GUI引擎和JS引擎是互斥的,这也是造成js阻塞的原因)

   事件触发线程:负责管理和调度页面中的事件 归属于浏览器而不是js引擎

   定时器触发线程:负责执行页面中的定时器 setInterval setTimeout

   网络请求线程: 当发起http请求时,会将请求发送给网络进程,由网络进程去和服务器通信,当网络进程接收到服务器的响应后,会将响应结果返回给异步http请求线程


assistant