0

0

探索下浏览器和 Node.js 为什么会这样设计 EventLoop!

青灯夜游

青灯夜游

发布时间:2022-01-05 10:29:57

|

2738人浏览过

|

来源于掘金社区

转载

本篇文章带大家探索下浏览器node.js 为什么会这样设计 eventloop,希望对大家有所帮助!

探索下浏览器和 Node.js 为什么会这样设计 EventLoop!

Event Loop 是 JavaScript 的基础概念,面试必问,平时也经常谈到,但是有没有想过为什么会有 Event Loop,它为什么会这样设计的呢?

今天我们就来探索下原因。

浏览器的 Event Loop

JavaScript 是用于实现网页交互逻辑的,涉及到 dom 操作,如果多个线程同时操作需要做同步互斥的处理,为了简化就设计成了单线程,但是如果单线程的话,遇到定时逻辑、网络请求又会阻塞住。怎么办呢?

可以加一层调度逻辑。把 JS 代码封装成一个个的任务,放在一个任务队列中,主线程就不断的取任务执行就好了。

每次取任务执行,都会创建新的调用栈。

1.png

其中,定时器、网络请求其实都是在别的线程执行的,执行完了之后在任务队列里放个任务,告诉主线程可以继续往下执行了。

2.png

因为这些异步任务是在别的线程执行完,然后通过任务队列通知下主线程,是一种事件机制,所以这个循环叫做 Event Loop。

这些在其他线程执行的异步任务包括定时器(setTimeout、setInterval),UI 渲染、网络请求(XHR 或 fetch)。

但是,现在的 Event Loop 有个严重的问题,没有优先级的概念,只是按照先后顺序来执行,那如果有高优先级的任务就得不到及时的执行了。所以,得设计一套插队机制。

那就搞一个高优先级的任务队列就好了,每执行完一个普通任务,都去把所有高优先级的任务给执行完,之后再去执行普通任务。

3.png

有了插队机制之后,高优任务就能得到及时的执行。

这就是现在浏览器的 Event Loop。

其中普通任务叫做 MacroTask(宏任务),高优任务叫做 MicroTask(微任务)。

宏任务包括:setTimeout、setInterval、requestAnimationFrame、Ajax、fetch、script 标签的代码。

微任务包括:Promise.then、MutationObserver、Object.observe。

怎么理解宏微任务的划分呢?

定时器、网络请求这种都是在别的线程跑完之后通知主线程的普通异步逻辑,所以都是宏任务。

而高优任务的这三种也很好理解,MutationObserver 和 Object.observe 都是监听某个对象的变化的,变化是很瞬时的事情,肯定要马上响应,不然可能又变了,Promise 是组织异步流程的,异步结束调用 then 也是很高优的。

这就是浏览器里的 Event Loop 的设计:设计 Loop 机制和 Task 队列是为了支持异步,解决逻辑执行阻塞主线程的问题,设计 MicroTask 队列的插队机制是为了解决高优任务尽早执行的问题。

但是后来,JS 的执行环境不只是浏览器一种了,还有了 Node.js,它同样也要解决这些问题,但是它设计出来的 Event Loop 更细致一些。

Node.js 的 Event loop

Node 是一个新的 JS 运行环境,它同样要支持异步逻辑,包括定时器、IO、网络请求,很明显,也可以用 Event Loop 那一套来跑。

但是呢,浏览器那套 Event Loop 就是为浏览器设计的,对于做高性能服务器来说,那种设计还是有点粗糙了。

哪里粗糙呢?

浏览器的 Event Loop 只分了两层优先级,一层是宏任务,一层是微任务。但是宏任务之间没有再划分优先级,微任务之间也没有再划分优先级。

而 Node.js 任务宏任务之间也是有优先级的,比如定时器 Timer 的逻辑就比 IO 的逻辑优先级高,因为涉及到时间,越早越准确;而 close 资源的处理逻辑优先级就很低,因为不 close 最多多占点内存等资源,影响不大。

于是就把宏任务队列拆成了五个优先级:Timers、Pending、Poll、Check、Close。

4.png

解释一下这五种宏任务:

Timers Callback: 涉及到时间,肯定越早执行越准确,所以这个优先级最高很容易理解。

Pending Callback:处理网络、IO 等异常时的回调,有的 *niux 系统会等待发生错误的上报,所以得处理下。

Poll Callback:处理 IO 的 data,网络的 connection,服务器主要处理的就是这个。

Check Callback:执行 setImmediate 的回调,特点是刚执行完 IO 之后就能回调这个。

触站AI
触站AI

专业的中文版AI绘画生成平台

下载

Close Callback:关闭资源的回调,晚点执行影响也不到,优先级最低。

所以呢,Node.js 的 Event Loop 就是这样跑的了:

5.png

还有一点不同要特别注意:

Node.js 的 Event Loop 并不是浏览器那种一次执行一个宏任务,然后执行所有的微任务,而是执行完一定数量的 Timers 宏任务,再去执行所有微任务,然后再执行一定数量的 Pending 的宏任务,然后再去执行所有微任务,剩余的 Poll、Check、Close 的宏任务也是这样。(订正:node 11 之前是这样,node 11 之后改为了每个宏任务都执行所有微任务了)

为什么这样呢?

其实按照优先级来看很容易理解:

假设浏览器里面的宏任务优先级是 1,所以是按照先后顺序依次执行,也就是一个宏任务,所有的微任务,再一个宏任务,再所有的微任务。

6.png

而 Node.js 的 宏任务之间也是有优先级的,所以 Node.js 的 Event Loop 每次都是把当前优先级的所有宏任务跑完再去跑微任务,然后再跑下一个优先级的宏任务。

7.png

也就是是一定数量的 Timers 宏任务,再所有微任务,再一定数量的 Pending Callback 宏任务,再所有微任务这样。

为什么说是一定数量呢?

因为如果某个阶段宏任务太多,下个阶段就一直执行不到了,所以有个上限的限制,剩余的下个 Event Loop 再继续执行。

除了宏任务有优先级,微任务也划分了优先级,多了一个 process.nextTick 的高优先级微任务,在所有的普通微任务之前来跑。

8.png

所以,Node.js 的 Event Loop 的完整流程就是这样的:

  • Timers 阶段:执行一定数量的定时器,也就是 setTimeout、setInterval 的 callback,太多的话留到下次执行
  • 微任务:执行所有 nextTick 的微任务,再执行其他的普通微任务
  • Pending 阶段:执行一定数量的 IO 和网络的异常回调,太多的话留到下次执行
  • 微任务:执行所有 nextTick 的微任务,再执行其他的普通微任务
  • Idle/Prepare 阶段:内部用的一个阶段
  • 微任务:执行所有 nextTick 的微任务,再执行其他的普通微任务
  • Poll 阶段:执行一定数量的文件的 data 回调、网络的 connection 回调,太多的话留到下次执行。如果没有 IO 回调并且也没有 timers、check 阶段的回调要处理,就阻塞在这里等待 IO 事件
  • 微任务:执行所有 nextTick 的微任务,再执行其他的普通微任务
  • Check 阶段:执行一定数量的 setImmediate 的 callback,太多的话留到下次执行。
  • 微任务:执行所有 nextTick 的微任务,再执行其他的普通微任务
  • Close 阶段:执行一定数量的 close 事件的 callback,太多的话留到下次执行。
  • 微任务:执行所有 nextTick 的微任务,再执行其他的普通微任务

比起浏览器里的 Event Loop,明显复杂了很多,但是经过我们之前的分析,也能够理解:

Node.js 对宏任务做了优先级划分,从高到低分别是 Timers、Pending、Poll、Check、Close 这 5 种,也对微任务做了划分,也就是 nextTick 的微任务和其他微任务。执行流程是先执行完当前优先级的一定数量的宏任务(剩下的留到下次循环),然后执行 process.nextTick 的微任务,再执行普通微任务,之后再执行下个优先级的一定数量的宏任务。。这样不断循环。其中还有一个 Idle/Prepare 阶段是给 Node.js 内部逻辑用的,不需要关心。

改变了浏览器 Event Loop 里那种一次执行一个宏任务的方式,可以让高优先级的宏任务更早的得到执行,但是也设置了个上限,避免下个阶段一直得不到执行。

还有一个特别要注意的点,就是 poll 阶段:如果执行到 poll 阶段,发现 poll 队列为空并且 timers 队列、check 队列都没有任务要执行,那么就阻塞的等在这里等 IO 事件,而不是空转。 这点设计也是因为服务器主要是处理 IO 的,阻塞在这里可以更早的响应 IO。

完整的 Node.js 的 Event Loop 是这样的:

9.png

对比下浏览器的 Event Loop:

10.png

两个 JS 运行环境的 Event Loop 整体设计思路是差不多的,只不过 Node.js 的 Event Loop 对宏任务和微任务做了更细粒度的划分,也很容易理解,毕竟 Node.js 面向的环境和浏览器不同,更重要的是服务端对性能的要求会更高。

总结

JavaScript 最早是用于写网页交互逻辑的,为了避免多线程同时修改 dom 的同步问题,设计成了单线程,又为了解决单线程的阻塞问题,加了一层调度逻辑,也就是 Loop 循环和 Task 队列,把阻塞的逻辑放到其他线程跑,从而支持了异步。然后为了支持高优先级的任务调度,又引入了微任务队列,这就是浏览器的 Event Loop 机制:每次执行一个宏任务,然后执行所有微任务。

Node.js 也是一个 JS 运行环境,想支持异步同样要用 Event Loop,只不过服务端环境更复杂,对性能要求更高,所以 Node.js 对宏微任务都做了更细粒度的优先级划分:

Node.js 里划分了 5 种宏任务,分别是 Timers、Pending、Poll、Check、Close。又划分了 2 种微任务,分别是 process.nextTick 的微任务和其他的微任务。

Node.js 的 Event Loop 流程是执行当前阶段的一定数量的宏任务(剩余的到下个循环执行),然后执行所有微任务,一共有 Timers、Pending、Idle/Prepare、Poll、Check、Close 6 个阶段。(订正:node 11 之前是这样,node 11 之后改为了每个宏任务都执行所有微任务了)

其中 Idle/Prepare 阶段是 Node.js 内部用的,不用关心。

特别要注意的是 Poll 阶段,如果执行到这里,poll 队列为空并且 timers、check 队列也为空,就一直阻塞在这里等待 IO,直到 timers、check 队列有回调再继续 loop。

Event Loop 是 JS 为了支持异步和任务优先级而设计的一套调度逻辑,针对浏览器、Node.js 等不同环境有不同的设计(主要是任务优先级的划分粒度不同),Node.js 面对的环境更复杂、对性能要求更高,所以 Event Loop 设计的更复杂一些。

更多node相关知识,请访问:nodejs 教程!!

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
node.js调试
node.js调试

node.js调试可以使用console.log()输出调试信息、断点调试和第三方调试工具。详细介绍:1、console.log()输出调试信息,通过在代码中插入console.log()语句,开发人员可以在控制台输出变量的值、函数的执行结果等信息,以便观察代码的执行流程和状态;2、断点调试,可以在代码中设置断点,以便在特定位置暂停代码的执行,观察变量的值和执行流程等。

362

2023.09.19

JavaScript 全栈开发基础(Node.js + 前端)
JavaScript 全栈开发基础(Node.js + 前端)

本专题系统介绍 JavaScript 在全栈开发中的核心知识结构,涵盖 Node.js 基础、Express/Koa 接口构建、前端交互设计、模块化与包管理、数据库连接、前后端数据通信与部署流程。通过完整项目示例,帮助学习者掌握从浏览器到服务器的一体化开发能力,实现真正意义上的全栈入门。

118

2025.11.26

Node.js后端开发与Express框架实践
Node.js后端开发与Express框架实践

本专题针对初中级 Node.js 开发者,系统讲解如何使用 Express 框架搭建高性能后端服务。内容包括路由设计、中间件开发、数据库集成、API 安全与异常处理,以及 RESTful API 的设计与优化。通过实际项目演示,帮助开发者快速掌握 Node.js 后端开发流程。

422

2026.02.10

ajax教程
ajax教程

php中文网为大家带来ajax教程合集,Ajax是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,Ajax可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。php中文网还为大家带来ajax的相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

166

2023.06.14

ajax中文乱码解决方法
ajax中文乱码解决方法

ajax中文乱码解决方法有设置请求头部的字符编码、在服务器端设置响应头部的字符编码和使用encodeURIComponent对中文进行编码。本专题为大家提供ajax中文乱码相关的文章、下载、课程内容,供大家免费下载体验。

170

2023.08.31

ajax传递中文乱码怎么办
ajax传递中文乱码怎么办

ajax传递中文乱码的解决办法:1、设置统一的编码方式;2、服务器端编码;3、客户端解码;4、设置HTTP响应头;5、使用JSON格式。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

124

2023.11.15

ajax网站有哪些
ajax网站有哪些

使用ajax的网站有谷歌、维基百科、脸书、纽约时报、亚马逊、stackoverflow、twitter、hacker news、shopify和basecamp等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

258

2024.09.24

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

443

2023.07.18

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

37

2026.03.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
如何进行WebSocket调试
如何进行WebSocket调试

共1课时 | 0.1万人学习

TypeScript全面解读课程
TypeScript全面解读课程

共26课时 | 5.1万人学习

前端工程化(ES6模块化和webpack打包)
前端工程化(ES6模块化和webpack打包)

共24课时 | 5.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号