nodejs里面的event loop

1. event loop

1.1 什么是event-loop

js的标准文档定义如下
https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#event_loop
https://javascript.info/event-loop

html的标准定义
https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model

nodejs自己实现了一套event-loop,与html/js的都不一样

  • https://github.com/nodejs/nodejs.dev/blob/main/src/documentation/0029-node-event-loop/index.md
  • https://nodejs.org/uk/docs/guides/event-loop-timers-and-nexttick/
  • https://stackoverflow.com/questions/31582672/what-is-the-different-between-javascript-event-loop-and-node-js-event-loop

1.2 nodejs的event-loop

官方文档的event-loop模型如下:
在这里插入图片描述

其中的关键节点说明

  • timers: this phase executes callbacks scheduled by setTimeout() and setInterval(). 此阶段执行setTimeout()和setInterval()里面的回调逻辑
  • pending callbacks: executes I/O callbacks deferred to the next loop iteration. 
    idle, prepare: only used internally. 内部使用的一个状态
  • poll: retrieve new I/O events; execute I/O related callbacks (almost all with the exception of close callbacks, the ones scheduled by timers, and setImmediate()); node will block here when appropriate.
  • check: setImmediate() callbacks are invoked here. 此阶段执行setImmediate()的回调
  • close callbacks: some close callbacks, e.g. socket.on(‘close’, …).

在一个I/O循环中,immediate() 总是比 timeout() 先执行 within an I/O cycle, the
immediate callback is always executed first than timeout callback:

参考:

  • https://hackernoon.com/arthurunderstanding-the-nodejs-event-loop-hin34zp
  • https://nodejs.org/uk/docs/guides/event-loop-timers-and-nexttick/

2. macrotask和microtask

在ES6中macro-task队列又称为ScriptJobs,而micro-task又称PromiseJobs
https://tc39.es/ecma262/#sec-jobs-and-job-queues

每个event loop都有一个macrotask和microtask
Each event loop has a microtask queue and a macrotask queue.

一个microtask是用来放入microstask queue里面的,而不是task queue(对应的是macrotask),参考如下文档
A microtask is a task that is originally to be queued on the microtask queue rather than a task queue. Refer to https://www.w3.org/TR/html51/webappapis.html#microtask-queue.

有2种类型的microtask(微任务)
There are two kinds of microtasks:

  • solitary callback microtasks, such as Promise, 单独的回调,如Promise
  • and compound microtasks, such as Object.observe, MutationObserver and process.nextTick in Node.js. 组合的microtask,如Object.observe, MutationObserver,和nodejs里面的process.nextTick

有如下类型的macrotask(宏任务)
And the macrotask queue mainly contains

  • setTimeout,
  • setInterval,
  • setImmediate,
  • requestAnimationFrame,
  • I/O in Nodejs.

在一个event-loop里面,2种task运行规则如下:
In a event Loop, these two task queues will run in two steps:

  • First, check whether there is a macrotask (call it X) in old macrotask queue ;
  • If X exists and it is running, wait for it to go to the next step until it was complete; otherwise, goto the next step immediately;
  • Second, run all microtasks of the microtask queue;
  • and when run the microtasks, we can still add some more microtaks into the queue, these tasks will also run.

The Call Stack is a fundamental part of the JavaScript language. It is a record-keeping structure that allows us to perform function calls. Each function call is represented as a frame on the Call Stack. This is how the JavaScript engine keeps track of which functions have been called and in what order. The JS engine uses this information to ensure execution picks back up in the right spot after a function returns.

When a JavaScript program first starts executing, the Call Stack is empty. When the first function call is made, a new frame is pushed onto the top of the Call Stack. When that function returns, its frame is popped off of the Call Stack.

The Event Loop is a looping algorithm that processes the Tasks/Microtasks in the Task Queue and Microtask Queue. It handles selecting the next Task/Microtask to be run and placing it in the Call Stack for execution.

The Event Loop algorithm consists of four key steps:

  1. Evaluate Script: Synchronously execute the script as though it were a function body. Run until the Call Stack is empty.
  2. Run a Task: Select the oldest Task from the Task Queue. Run it until the Call Stack is empty.
  3. Run all Microtasks: Select the oldest Microtask from the Microtask Queue. Run it until the Call Stack is empty. Repeat until the Microtask Queue is empty.
  4. Rerender the UI: Rerender the UI. Then, return to step 2. (This step only applies to browsers, not NodeJS).

Let’s model the Event Loop with some JavaScript psuedocode:

while (EventLoop.waitForTask()) {const taskQueue = EventLoop.selectTaskQueue();if (taskQueue.hasNextTask()) {taskQueue.processNextTask();}const microtaskQueue = EventLoop.microTaskQueue;while (microtaskQueue.hasNextMicrotask()) {microtaskQueue.processNextMicrotask();}rerender();
}

参考文档:

  • https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide
  • https://stackoverflow.com/questions/25915634/difference-between-microtask-and-macrotask-within-an-event-loop-context?rq=1
  • https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide/In_depth
  • https://medium.com/dkatalis/eventloop-in-nodejs-macrotasks-and-microtasks-164417e619b9
  • https://stackoverflow.com/questions/46375711/what-is-the-relationship-between-event-loop-and-promise

3. 执行时序

3.1 整体的执行时序

script(主程序代码)—>process.nextTick—>Promises…——>setTimeout——>setInterval——>setImmediate——> I/O——>UI rendering
setTimeout(function(){console.log(1)},0);

// 异步创建
Promise.resolve().then(function () {console.log(22);});// 同步创建
new Promise(function(resolve,reject){console.log(2);resolve(true);
}).then(function(){console.log(3)
}).then(function(){console.log(4)});process.nextTick(function(){console.log(5)});new Promise(function(resolve,reject){console.log(7);resolve(true);});console.log(6);class Studuent {private mAge:number = 0;constructor(age:number) {this.mAge = ageconsole.log("age");}
}new Studuent(10);

执行结果如下:

2
7
6
age
5
22
3
4
1

说明:

  • 首先执行的是new Promise构造方法里面的代码,按js代码的执行顺序,输出2
  • 然后输出的是7,主要是用来测试new Promise构造方和process.nextTick的顺序
  • 第3个输出的是6,按代码的顺序
  • 第4个输出的是age,主要是用来验证new Promise构造方法,原理都类似,代码按顺序执行过来,执行构造方法里面的逻辑
  • 第5个输出的是5,执行process.nextTick,是在本次代码执行完,准备执行后续event-loop之前执行
  • 第6个输出的是22,走第1个promise
  • 第7个和第8个,依次走后续的promise
  • 第9个输出是1,走的是setTimeout,在下一次macrotask里面执行

再看一个特殊一点的例子

process.nextTick(() => console.log(1));
Promise.resolve().then(() => console.log(2));
Promise.resolve().then(() => {console.log(3);process.nextTick(() => console.log(4));Promise.resolve().then(() => console.log(5));
}).then(() => {console.log(6);
})

执行结果

1
2
3
5
6
4

特殊的地方在4,当执行以上代码的时候
When we run the code:

  • callback in process.nextTick is added to process.nextTick queue.
  • callback in promises are added to promises microtask queue.

Event loop executes tasks in following order: process.nextTick queue, promises microtask queue, setTimeout queue, setImmediate queue

  • Event loop will execute callback from process.nextTick queue and prints 1.
  • Event loop will look for promises microtask queue and then processes them. So it prints 2 and 3. On the next line it again adds a callback to process.nextTick queue which will be executed once promises microtask queue is finished.
  • So the next 5 and 6 will be printed. Now promises microtask queue is empty
  • Event loop will again check if there is any handler in process.nextTick queue and will execute them. So it now prints 4.

参考:

  • https://medium.com/dkatalis/eventloop-in-nodejs-macrotasks-and-microtasks-164417e619b9
  • https://medium.com/dkatalis/eventloop-in-nodejs-settimeout-setimmediate-vs-process-nexttick-37c852c67acb
  • https://developer.aliyun.com/article/613411
  • https://github.com/nodejs/help/issues/1789

3.2 process.nextTick和setImmediate

nextTick是在setImmediate之前执行的

在上面的nodejs的event-loop图里面,process.nextTick()里面的回调会在每次event-loop继续执行之前执行
Looking back at our diagram, any time you call process.nextTick() in a given phase, all callbacks passed to process.nextTick() will be resolved before the event loop continues.

使用process.nextTick()方法,可以保证我们的代码可以在用户代码执行完之后且在event-loop事件开始前执行
By using process.nextTick() we guarantee that apiCall() always runs its callback after the rest of the user’s code and before the event loop is allowed to proceed.

function apiCall(arg, callback) {if (typeof arg !== 'string')return process.nextTick(callback,new TypeError('argument should be string'));
}

关于这2个方法的命名,其实有一些歧义,nodejs的开发也有如下解释:

Note that nextTick is opposite of “in the next tick”. It’s “in the
current tick but after all synchronous code and previously queued
nextTicks finished”.

setImmediate is the opposite of immediately - it runs in the next tick
😅 .

Their naming is pretty confusing. setImmediate is already promisified
and people can use that to await for I/O to run.

参考:

  • https://github.com/nodejs/node/issues/19617
  • https://github.com/nodejs/node/pull/38393
  • https://medium.com/dkatalis/eventloop-in-nodejs-settimeout-setimmediate-vs-process-nexttick-37c852c67acb
  • https://github.com/nodejs/nodejs.dev/blob/main/src/documentation/0030-node-process-nexttick/index.md
  • https://nodejs.org/uk/docs/guides/event-loop-timers-and-nexttick/

3.3 setImmediate()和setTimeout()

2个方法都类似,差异的地方在于他们调用的时机

  • 在非I/O环境下,setTimeout在setImmediate之前执行
  • 在I/O环境下,setImmediate在setTimeout之前执行

在非I/O环境下

// timeout_vs_immediate.js
setTimeout(() => {console.log('timeout');
}, 0);setImmediate(() => {console.log('immediate');
});

执行结果是(多次执行的结果是一样的,和里面说的有点不一样,可能是执行环境的原因吧https://nodejs.org/uk/docs/guides/event-loop-timers-and-nexttick/)

$ node timeout_vs_immediate.js
timeout
immediate

在I/O环境下

// timeout_vs_immediate.js
const fs = require('fs');fs.readFile(__filename, () => {setTimeout(() => {console.log('timeout');}, 0);setImmediate(() => {console.log('immediate');});
});

执行结果是:

$ node timeout_vs_immediate.js
immediate
timeout

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/1621534.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:如何用Python编写炒股软件

如何用Python编写炒股软件 随着股票投资的普及和互联网技术的发展,越来越多的人开始尝试使用计算机辅助炒股,以获取更好的投资回报。Python作为一种简单易学、高效实用的编程语言,受到了众多股票投资者的青睐。本文将介绍如何用Python编写炒…

单例模式:保证一个类仅有一个实例

单例模式:保证一个类仅有一个实例 欢迎来到设计模式系列的第二篇文章!在上一篇中,我们已经对设计模式有了初步的了解,今天让我们深入研究第一个模式——单例模式。 什么是单例模式? 在软件开发中,我们经…

RabbitMQ从原理到实战—基于Golang【万字详解】

文章目录 前言一、MQ是什么?优势劣势 二、MQ的用途1、应用解耦2、异步加速3、削峰填谷4、消息分发 三、RabbitMQ是什么1、AMQP 协议2、RabbitMQ 包含的要素3、RabbitMQ 基础架构 四、实战1、Simple模式(即最简单的收发模式)2、Work Queues 模型3、Publish/Subscribe…

lnmp架构-mysql

1.MySQL数据库编译 make完之后是这样的 mysql 初始化 所有这种默认不在系统环境中的路径里 就这样加 这样就可以直接调用 不用输入路径调用 2.初始化 重置密码 3.mysql主从复制 配置master 配置slave 当master 端中还没有插入数据时 在server2 上配slave 此时master 还没进…

Tornado服务器连接数据库

环境 python3.6.1 vscode mysql navicat 安装需要的包 pip install torndb_for_python3 pip install pymysql0.8.0 #请安装pymysql的0.8.0版本,否则可能出现一个FLAG不存在的bug。亲测0.8.0可用。 tornadb不适用于python3,torndb_for_python3 是修改过的…

Tornado框架入门教程

Tornado框架入门教程 Tornado在知乎广为使用,当你用Chrome打开网页版本的知乎,使用开发者工具仔细观察Network里面的请求,就会发现有一个特别的状态码为101的请求,它是用浏览器的websocket技术和后端服务器建立了长连接用来接收服…

Tornado自定义路由

默认路由规则 代码 由下述代码可知,路由规则都在放在 tornado.web.Application 中,强迫症的我看着不舒服不说,而且如果有多级路由的话就很难搞了,比如说用户模块、不同视图模块等等,前一级路由是固定的,后…

Tornado重定向(三)

为什么需要重定向呢?比较常见的就是当网站内部结构变动,如栏目、网址等等的变动就需要进行301重定向,还有内部一些错误的页面也可以做301重定向,提高用户体验。当因网站内部结构变动,需要删除网站中的某些目录时&#…

Tornado介绍

文章目录 特点结构Tornado实现异步原理模块Tornado服务器的三个底层核心模块设计模型 Tornado龙卷风是一个开源的网络服务器框架,它是基于社交聚合网站FriendFeed的实时信息服务开发而来的 Tornado是使用Python编写的Web服务器兼Web应用框架与主流Web服务器框架不同…

Tornado初见(一)

在之前的其他博客中介绍了Django,这里介绍一下Tornado。两者的区别可以总结为: django大而全,适合小型的压力不大的项目,一旦压力上来其是扛不住的,毕竟一是太重,而是非异步。 但是好处就是什么都有&#…

Tornado框架学习

目录 底层原理 select epoll 运行机制 Tornado中的Ioloop模块的作用 获取请求方式 文件上传与展示 通过请求头信息的判断来进行反爬验证 注册功能demo 重定向 用户登录 以及自己设置的错误界面跳转 Cookie Tornado 异步服务器端方式 客户端异步请求 Tornado基于epoll…

tornado入门必备知识总结——异步事件循环与协程

文章目录 前言同步、异步、阻塞和非阻塞socket的非阻塞io请求htmlselect、poll和epoll协程异步http请求tornado实现高并发的爬虫 前言 要想走得远,基础就得牢,路漫漫其修远兮,吾将上下而求索。 tornado简介 python web编程三剑客Django&…

【Tornado】Tornado入门教程

目录 Tornado特点结构三个底层核心模块 安装1. 安装python32. 安装tornado3. 编写简单server4. 运行流程 核心组件1. ioloop实例2. app实例3. urls路由表4. handler类 异步协程async 和 await如何工作的怎样调用 协程模式结合 callbacks调用阻塞函数交叉存取技术循环在后台运行…

【数据结构与算法篇】手撕八大排序算法之交换排序

​👻内容专栏: 《数据结构与算法篇》 🐨本文概括:常见交换排序包括冒泡排序与快速排序,本篇讲述冒泡排序与快速排序的思想及实现、复杂度分析。 🐼本文作者: 花 蝶 🐸发布时间&#…

ie ajax十分卡,解决jquery .ajax 在IE下卡死问题的解决方法

解决jquery .ajax 在IE下卡死问题的解决方法 解决IE编码问题第一步: dataType:($.browser.msie) ? "text" : "xml" 先这样做让IE 识别返回的是text 还是xml 第二步: 复制代码 代码如下: function parseXml(xml) { //XML IE编码问题…

数学分析:场论

我们之前知道的是里斯表示定理。 这里看到,对于多重线性映射,里斯表示定理会从内积变成混合积。当然我们还是只考虑三维以内的情况。 于是我们可以把不同的1形式和2形式的下标写上,表示他们相当于内积或者混合积对应的那个向量。 然后还差0形…

设计师:设计师之家装材料知识之家装八项(吊顶材料、门窗材料、五金材料、墙面材料、地面材料、胶粘材料、油漆材料、水电材料等)之详细攻略

设计师:设计师之家装材料知识之家装八项(吊顶材料、门窗材料、五金材料、墙面材料、地面材料、胶粘材料、油漆材料、水电材料等)之详细攻略 目录 家装八项 吊顶材料 门窗材料 五金材料 墙面材料 地面材料 胶粘材料 油漆材料 水电材料 参考文章:…

材料封样信息流程指引

材料封样信息流程指引 一、材料封样流程 1、新项目中标后,由设计院根据合同、招标文件、技术要求填写材料送样清单(格式按公司标准化标格填写),并正式下发给采购部、工程管理部及项目部安排后续送样确认; 2、幕墙工程…

EyouCMS响应式木材板材公司模板/易优CMS装修材料类企业网站模板

☑️ 编号:ym247 ☑️ 品牌:EyouCMS ☑️ 语言:PHP ☑️ 大小:22.4MB ☑️ 类型:木材板材公司模板 ☑️ 支持:pcwap 🎉 欢迎免费领取(注明编号) 🎉 ✨ 源码介…

2023最新装修材料石膏线品牌加盟类模板源码+织梦内核开发的

正文: 装修材料石膏线品牌加盟类织梦模板,带手机版数据同步。 效果相当的炫酷,相当简洁大气高端。适用于建材网站源码、石英石网站模版,有兴趣的自行去安装体验吧,其它就没什么好介绍的了。 程序: wweohd.lanzouo.com/iRCs80t…