关于Nodejs的事件循环Event Loop,网上有各种各样的介绍,因此本文我们不再针对具体的事件循环进行说明,我们从一个实际的示例来说明Nodejs的事件循环究竟是如何工作。

背景

Nodejs 事件循环是支撑Nodejs 非阻塞IO以及异步执行的基础,因此理解事件循环的执行也就可以写出正确的代码或者说我们就能更快的找出为什么不按我写的代码顺序执行的原因所在。

说起Nodejs事件循环,不得不提一个图,介绍的是Nodejs的事件循环示意图,其中将事件循环分为:timers、pending callback、idle prepare 、poll、check、close。

其中本文我们仅关注三个阶段,timers、poll、check。

其中timers阶段对应的是setTimeout、setInterval 两个方法,而这两个方法对应的也是宏任务。

poll 是异步回调事件,除了setTimeout、setInterval、process.nextTick、Promise、setImmediate之外的事件。

check 阶段对应的是serImmediate 事件。

下面我们画一个表格来说明这三个阶段:

阶段 方法 是否宏任务 所属队列
timers setTimeout、setIntrval 宏任务队列
poll 各种异步回调    
check setImmediate 宏任务队列

另外,process.nextTick 属于微任务TickQueue队列,Promise.resolve()属于others 微任务队列。

说到这里,有小伙伴会想,说好的事件循环,咋还聊起来宏任务、微任务了,文不对题,差评。

别急,老铁,之所以会提微任务,是因为微任务也会参与Nodejs的异步执行,因此加上这俩微任务,我们就可以更明确的说明程序代码的执行顺序。

上酸菜,

示例

先简要说明下结论,微任务执行队列是按先进先出的顺序执行,但微任务队列之间是有执行顺序,nextTick queue的执行优先级大于others 微任务队列。

另外,微任务会比宏任务先执行。

示例1:如何让一个同步执行的方法延迟执行?

1 let carName;
2 function myCar() {
3     // 此处需要延迟执行 否则carName会输出undefined
4     console.log("this is mycar: " + carName);
5 }
6 
7 function setCarName() {
8     carName = "Audi";
9 }

参考做法,将myCar的执行延迟,

 1 let carName;
 2 function myCar() {
 3     // 此处需要延迟执行 否则carName会输出undefined
 4     process.nextTick(()=>{
 5         console.log("this is mycar: " + carName);
 6     });    
 7     setTimeout(() => {
 8         console.log("this is mycar: " + carName);
 9     });
10     Promise.resolve().then(()=>{
11         console.log("this is mycar: " + carName);
12     });
13 
14     setImmediate(()=>{
15         console.log("this is mycar: " + carName);
16     });
17 }
18 
19 function setCarName() {
20     carName = "Audi";
21 }
22 
23 myCar();
24 setCarName();

示例2:宏任务和微任务混合代码,在Nodejs 事件循环的加持下,会产生怎样的火花?

先说明几个原则,

1、在有同步代码执行的时候,同步代码的执行优先级会高于任何的异步代码

2、而异步代码如果分属不同的阶段,那么执行顺序也是固定

3、在同一执行背景下,微任务代码的优先级会高于宏任务代码。

简单示例1:包含单个阶段的异步代码

//同步代码优先执行 同步代码的执行优先级最高
console.log("start");
myCar(1);

//会加入到事件循环timers阶段
setTimeout(() => {
    console.log("timeout 1");
});

//会加入到事件循环timers阶段
setTimeout(() => {
    console.log("timeout 2");
});


myCar(2);
console.log("end");

function myCar(index) {
    console.log("同步输出" + index);
}

通过该示例,我们会得出一个结论,在同一个事件循环阶段,会按照添加顺序,先进先出的原则进行执行。

 

简单示例2:加入微任务执行

加入process.NextTick及Promise 微任务队列,请注意我们前面提到,微任务队列的执行优先级高于宏任务队列,且nextTickqueue的执行优先级高于Promise所属的others。

 1 //同步代码优先执行 同步代码的执行优先级最高
 2 console.log("start");
 3 myCar(1);
 4 
 5 //会加入到事件循环timers阶段
 6 setTimeout(() => {
 7     console.log("timeout 1");
 8 });
 9 
10 //会加入到事件循环timers阶段
11 setTimeout(() => {
12     console.log("timeout 2");
13 });
14 
15 // 会添加到微任务队列 nextTickQueue 队列中
16 process.nextTick(() => {
17     console.log("tick 1");
18 });
19 
20 //会添加到微任务队列 others 微任务队列
21 Promise.resolve().then(() => {
22     console.log("promise 1");
23 });
24 
25 // 会添加到微任务队列 nextTickQueue 队列中
26 process.nextTick(() => {
27     console.log("tick 2");
28 });
29 //会添加到微任务队列 others 微任务队列
30 Promise.resolve().then(() => {
31     console.log("promise 2");
32 });
33 
34 myCar(2);
35 console.log("end");
36 
37 function myCar(index) {
38     console.log("同步输出" + index);
39 }

 

结论

本文主要描述的是Nodejs 微任务与宏任务执行的先后顺序,更多具体的执行细节,请讲上述示例手工敲代码运行之后,仔细回味,针对方法内部包含的也是同理。

1、微任务执行>宏任务任务

2、nextTick >others 执行

3、同步执行代码执行优先级最高。

4、同一阶段或同一队列的执行,按先进先出的顺序执行。

5、代码的执行和代码所在的位置无关

 

根据我最近的学习,总结出一个结论,那就是Nodejs 入门简单,了解原理很难,加油,少年。

posted @ 2021-03-26 17:59 baidixing 阅读(1298) 评论(0) 推荐(0) 编辑
摘要: Nestjs 阅读全文
posted @ 2021-02-06 19:48 baidixing 阅读(1234) 评论(0) 推荐(0) 编辑
摘要: NodeJS允许我们发布自己的npm包,且可以执行我们自定义的command。大部分时候,运行非常好,但如果一个新手或客户电脑运行你发布的npm 包 cmd,发现出现了 : ** 不是内部或外部命令,也不是可运行的程序或批处理文件。我们的第一反应肯定是,我电脑没问题呀。 本着出现问题,解决问题,深究 阅读全文
posted @ 2020-04-25 19:39 baidixing 阅读(4032) 评论(0) 推荐(1) 编辑
摘要: angular 路由功能非常强大,同时angular的路由也非常脆弱,非常容易出现错误。 那么在我们遇到异常时,首先要做的是什么? 第一步:检查代码,对比官方文档,发现代码中不一致的地方进行改正。 第二步:调试代码,观察调用过程中参数传递是否正常。 第三步:百度一下。 对于我这个观点,可能会有人不服 阅读全文
posted @ 2019-10-31 11:55 baidixing 阅读(1881) 评论(0) 推荐(0) 编辑
摘要: Nodejs支持跨平台运行,基本可以运行于所有的操作系统,对应不同的操作系统,安装方式也不同。 Nodejs的官方网站:https://nodejs.org 对应的中文网站:http://nodejs.cn/ 倾向于英文网站,中文网站毕竟会有很大的延迟。 Windows系统的安装:不再赘述。下载对应 阅读全文
posted @ 2019-09-25 17:27 baidixing 阅读(491) 评论(0) 推荐(0) 编辑
摘要: 如果RabbitMQ集群只有一个broker节点,那么该节点的失效将导致整个服务临时性的不可用,并且可能会导致message的丢失(尤其是在非持久化message存储于非持久化queue中的时候)。可以将所有message都设置为持久化,并且使用持久化的queue,但是这样仍然无法避免由于缓存导致的 阅读全文
posted @ 2018-12-28 11:19 baidixing 阅读(4516) 评论(3) 推荐(4) 编辑
摘要: RabbitMQ集群部署完成,通过HAProxy反向代理来提供统一的对RabbitMQ的访问入口。 1.Haproxy提供高可用性、负载均衡,以及基于TCP和HTTP的应用程序代理。(负载均衡策略有很多:轮询、加权轮询、源地址哈希、最小连接数等等) 2.为什么使用集群?面对大量业务访问、高并发请求可 阅读全文
posted @ 2018-12-27 19:26 baidixing 阅读(1039) 评论(0) 推荐(0) 编辑
摘要: 随着公司业务量的增加,原本部署在Windows服务器的RabbitMQ集群(3.6.1)总是出现莫名其妙的问题,经查询官方Issue,确认是RabbitMQ 3.6.1 版本的bug。查看从3.6.1 版本至 3.7.9 版本的变更日志,可以发现RabbitMQ官方修复了不少bug,本着版本越新 b 阅读全文
posted @ 2018-12-25 17:46 baidixing 阅读(1780) 评论(1) 推荐(2) 编辑
摘要: 上篇我们提到不使用RPM安装RabbitMQ 3.7.8,其实我个人更倾向不使用RPM安装RabbitMQ,因为可以控制安装位置及设置参数。 存在即合理,使用RPM安装RabbitMQ,可以减少配置参数的烦恼,使用RPM之前建议先通过不使用RPM的方式安装,以便了解各种参数及其含义。 安装Rabbi 阅读全文
posted @ 2018-12-24 15:45 baidixing 阅读(1765) 评论(0) 推荐(0) 编辑
摘要: 每种语言都会有字符串的操作,因为字符串是我们平常开发使用频率最高的一种类型。今天我们来聊一下Java的字符串操作及在某些具体方法中与C#的不同,对于需要熟悉多种语言的人来说,作为一种参考。进行诫勉 首先,什么是字符串? 字符串是字符的序列,是作为一种对象而存在。说的直白点,字符串就是一些字符的组合, 阅读全文
posted @ 2018-12-03 23:39 baidixing 阅读(1619) 评论(1) 推荐(0) 编辑
点击右上角即可分享
微信分享提示