Node.js Event Loop 的理解 Timers,process.nextTick()
写这篇文章的目的是将自己对该文章的理解做一个记录,官方文档链接The Node.js Event Loop, Timers, and process.nextTick()
文章内容可能有错误理解的地方,希望能和大家探讨一下,欢迎批评指正!
Node.js Event Loop 的理解 Timers,process.nextTick()
Event Loop的解释
英文原文: When Node.js starts, it initializes the event loop, processes the provided input script (or drops into the REPL, which is not covered in this document) which may make async API calls, schedule timers, or call process.nextTick(), then begins processing the event loop.
当Node.js启动时会初始化event loop, 每一个event loop都会包含按如下顺序六个循环阶段,
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
- timers 阶段: 这个阶段执行setTimeout(callback) and setInterval(callback)预定的callback;
- I/O callbacks 阶段: 执行除了 close事件的callbacks、被timers(定时器,setTimeout、setInterval等)设定的callbacks、setImmediate()设定的callbacks之外的callbacks;
- idle, prepare 阶段: 仅node内部使用;
- poll 阶段: 获取新的I/O事件, 适当的条件下node将阻塞在这里;
- check 阶段: 执行setImmediate() 设定的callbacks;
- close callbacks 阶段: 比如socket.on(‘close’, callback)的callback会在这个阶段执行.
每一个阶段都有一个装有callbacks的fifo queue(队列),当event loop运行到一个指定阶段时, node将执行该阶段的fifo queue(队列),当队列callback执行完或者执行callbacks数量超过该阶段的上限时, event loop会转入下一下阶段.
注意上面六个阶段都不包括 process.nextTick()
poll阶段
poll阶段是衔接整个event loop各个阶段比较重要的阶段,为了便于后续例子的理解,本文和原文的介绍顺序不一样,本文先讲这个阶段;
在node.js里,任何异步方法(除timer,close,setImmediate之外)完成时,都会将其callback加到poll queue里,并立即执行。
poll 阶段有两个主要的功能:
- 处理poll队列(poll quenue)的事件(callback);
- 执行timers的callback,当到达timers指定的时间时;
如果event loop进入了 poll阶段,且代码未设定timer,将会发生下面情况:
- 如果poll queue不为空,event loop将同步的执行queue里的callback,直至queue为空,或执行的callback到达系统上限;
- 如果poll queue为空,将会发生下面情况:
- 如果代码已经被setImmediate()设定了callback, event loop将结束poll阶段进入check阶段,并执行check阶段的queue (check阶段的queue是 setImmediate设定的)
- 如果代码没有设定setImmediate(callback),event loop将阻塞在该阶段等待callbacks加入poll queue;
如果event loop进入了 poll阶段,且代码设定了timer:
- 如果poll queue进入空状态时(即poll 阶段为空闲状态),event loop将检查timers,如果有1个或多个timers时间时间已经到达,event loop将按循环顺序进入 timers 阶段,并执行timer queue.
以上便是整个event loop时间循环的各个阶段运行机制,有了这层理解,我们来看几个例子
- 注意,例子中给出的时间在不同机器下和同一机器下不同执行时刻,其值都会有差异。
example 1
var fs = require('fs');
function someAsyncOperation (callback) {
// 花费2毫秒
fs.readFile(__dirname + '/' + __filename, callback);
}
var timeoutScheduled = Date.now();
var fileReadTime = 0;
setTimeout(function () {
var delay = Date.now() - timeoutScheduled;
console.log('setTimeout: ' + (delay) + "ms have passed since I was scheduled");
console.log('fileReaderTime',fileReadtime - timeoutScheduled);
}, 10);
someAsyncOperation(function () {
fileReadtime = Date.now();
while(Date.now() - fileReadtime < 20) {
}
});
结果: 先执行someAsyncOperation的callback,再执行setTimeout callback
-> node eventloop.js
setTimeout: 22ms have passed since I was scheduled
fileReaderTime 2
解释: 当时程序启动时,event loop初始化:
- timer阶段(无callback到达,setTimeout需要10毫秒)
- i/o callback阶段,无异步i/o完成
- 忽略
- poll阶段,阻塞在这里,当运行2ms时,fs.readFile完成,将其callback加入 poll队列,并执行callback, 其中callback要消耗20毫秒,等callback之行完,poll处于空闲状态,由于之前设定了timer,因此检查timers,发现timer设定时间是20ms,当前时间运行超过了该值,因此,立即循环回到timer阶段执行其callback,因此,虽然setTimeout的20毫秒,但实际是22毫秒后执行。
example 2
var fs = require('fs');
function someAsyncOperation (callback) {
var time = Date.now();
// 花费9毫秒
fs.readFile('/path/to/xxxx.pdf', callback);
}
var timeoutScheduled = Date.now();
var fileReadTime = 0;
var delay = 0;
setTimeout(function () {
delay = Date.now() - timeoutScheduled;
}, 5);
someAsyncOperation(function () {
fileReadtime = Date.now();
while(Date.now() - fileReadtime < 20) {
}
console.log('setTimeout: ' + (delay) + "ms have passed since I was scheduled");
console.log('fileReaderTime',fileReadtime - timeoutScheduled);
});
结果:setTimeout callback先执行,someAsyncOperation callback后执行
-> node eventloop.js
setTimeout: 7ms have passed since I was scheduled
fileReaderTime 9
解释: 当时程序启动时,event loop初始化:
- timer阶段(无callback到达,setTimeout需要10毫秒)
- i/o callback阶段,无异步i/o完成
- 忽略
- poll阶段,阻塞在这里,当运行5ms时,poll依然空闲,但已设定timer,且时间已到达,因此,event loop需要循环到timer阶段,执行setTimeout callback,由于从poll --> timer中间要经历check,close阶段,这些阶段也会消耗一定时间,因此执行setTimeout callback实际是7毫秒 然后又回到poll阶段等待异步i/o完成,在9毫秒时fs.readFile完成,其callback加入poll queue并执行。
setTimeout 和 setImmediate
二者非常相似,但是二者区别取决于他们什么时候被调用.
- setImmediate 设计在poll阶段完成时执行,即check阶段;
- setTimeout 设计在poll阶段为空闲时,且设定时间到达后执行;但其在timer阶段执行
其二者的调用顺序取决于当前event loop的上下文,如果他们在异步i/o callback之外调用,其执行先后顺序是不确定的
setTimeout(function timeout () {
console.log('timeout');
},0);
setImmediate(function immediate () {
console.log('immediate');
});
$ node timeout_vs_immediate.js
timeout
immediate
$ node timeout_vs_immediate.js
immediate
timeout
关于这点的原因,笔者目前还未弄清楚。。。其原因读者可去看@hyj1991 的留言,讲解非常清晰
但当二者在异步i/o callback内部调用时,总是先执行setImmediate,再执行setTimeout
var fs = require('fs')
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout')
}, 0)
setImmediate(() => {
console.log('immediate')
})
})
$ node timeout_vs_immediate.js
immediate
timeout
理解了event loop的各阶段顺序这个例子很好理解: 因为fs.readFile callback执行完后,程序设定了timer 和 setImmediate,因此poll阶段不会被阻塞进而进入check阶段先执行setImmediate,后进入timer阶段执行setTimeout
process.nextTick()
千呼万唤始出来,终于到了讲process.nextTick()的时候,来来来喝口水休息休息。
注意:本文讲解的process.nextTick是基于v0.10及以上版本
process.nextTick()不在event loop的任何阶段执行,而是在各个阶段切换的中间执行,即从一个阶段切换到下个阶段前执行。
var fs = require('fs');
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('setTimeout');
}, 0);
setImmediate(() => {
console.log('setImmediate');
process.nextTick(()=>{
console.log('nextTick3');
})
});
process.nextTick(()=>{
console.log('nextTick1');
})
process.nextTick(()=>{
console.log('nextTick2');
})
});
-> node eventloop.js
nextTick1
nextTick2
setImmediate
nextTick3
setTimeout
从poll —> check阶段,先执行process.nextTick, nextTick1 nextTick2 然后进入check,setImmediate, setImmediate 执行完setImmediate后,出check,进入close callback前,执行process.nextTick nextTick3 最后进入timer执行setTimeout setTimeout
process.nextTick()是node早期版本无setImmediate时的产物,node作者推荐我们尽量使用setImmediate。
example process.nextTick()
当递归调用process.nextTick时,即使fs.readFile完成,其callback无机会执行
var fs = require('fs');
var starttime = Date.now();
var endtime = 0;
fs.readFile(__filename, () => {
endtime = Date.now();
console.log('finish reading time:',endtime - starttime);
});
var index = 0;
function nextTick () {
if (index > 1000) return;
index++;
console.log('nextTick');
process.nextTick(nextTick);
}
nextTick();
-> node eventloop.js
nextTick
nextTick
...
nextTick
nextTick
finish reading time: 246
example setImmediate
将process.nextTick替换成setImmediate后,由于setImmediate只在check阶段执行,那么所有的callback都有机会执行。
var fs = require('fs');
fs.readFile(__filename, () => {
console.log('finish reading');
});
var index = 0;
function Immediate () {
if (index > 100) return;
index++;
console.log('setImmediate');
setImmediate(Immediate);
}
Immediate()
-> node eventloop.js
setImmediate
setImmediate
setImmediate
setImmediate
finish reading time: 19
...
setImmediate
setImmediate
Node.js Event Loop 的理解 Timers,process.nextTick()的更多相关文章
- The Node.js Event Loop, Timers, and process.nextTick() Node.js事件循环,定时器和process.nextTick()
个人翻译 原文:https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/ The Node.js Event Loop, Ti ...
- The Node.js Event Loop, Timers, and process.nextTick()
The Node.js Event Loop, Timers, and process.nextTick() | Node.js https://nodejs.org/uk/docs/guides/e ...
- Node.js event loop 和 JS 浏览器环境下的事件循环的区别
Node.js event loop 和 JS 浏览器环境下的事件循环的区别: 1.线程与进程: JS 是单线程执行的,指的是一个进程里只有一个主线程,那到底什么是线程?什么是进程? 进程是 CPU ...
- [译]Node.js - Event Loop
介绍 在读这篇博客之前,我强列建议先阅读我的前两篇文章: Getting Started With Node.js Node.js - Modules 在这篇文章中,我们将学习 Node.js 中的事 ...
- Node.js学习笔记:setImmediate与process.nextTick
通过process.nextTick注册的函数在当前这个事件循环中执行的函数执行完毕后立即执行,相当于把当前的同步代码执行完毕之后,立刻执行所有的通过process.nextTick注册的函数,如果注 ...
- JS event loop
一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊. Java ...
- 对Node.JS的事件轮询(Event Loop)的理解
title: Node.JS的事件轮询(event loop)的理解 categories: 理解 tags: Node JS 机制 当我们知道I/O操作和创建新线程的开销是巨大的! 网站延迟的开销 ...
- Node.js机制及原理理解初步【转】
一.node.js优缺点 node.js是单线程. 好处就是 1)简单 2)高性能,避免了频繁的线程切换开销 3)占用资源小,因为是单线程,在大负荷情况下,对内存占用仍然很低 3)线程安全,没有加锁. ...
- 关于Node.js的__dirname,__filename,process.cwd(),./文件路径的一些坑
探索 计算机不会欺骗人,一切按照规则执行,说找不到这个文件,那肯定就是真的找不到,至于为什么找不到,那就是因为我们理解有偏差,我最初理解的'./'是当前执行js文件所在的文件夹的绝对路径,然后Node ...
随机推荐
- 乘风破浪:LeetCode真题_033_Search in Rotated Sorted Array
乘风破浪:LeetCode真题_033_Search in Rotated Sorted Array 一.前言 将传统的问题进行一些稍微的变形,这个时候我们可能无所适从了,因此还是实践出真知, ...
- 打开word文档时提示“Microsoft Office Word已停止工作”
我的电脑(Win10)有Office 2003和2013两个版本,可能由于之前超长待机等原因导致word 2003的文件(.doc)不能正常打开,没次都会提示“Microsoft Office Wor ...
- react-native-storage 使用笔记 持续更新
React-native-storage是在AsyncStorage之上封装的一个缓存操作插件库,刚开始接触这个也遇到了一些问题,在这里简单记录总结一下,碰到了就记下来,持续更新吧 1.安卓下stor ...
- 【Java集合源代码剖析】ArrayList源代码剖析
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/mmc_maodun/article/details/35568011 转载请注明出处:http:// ...
- NOIP模拟赛-2018.10.22
模拟赛 今天第一节课是历史,当然是不可能上的,一来到机房发现今天高二考试... 老师说以后可能还要给高一考...那还不如现在跟着做好了,毕竟在学长学姐中垫底显得没那么丢人 这套题风格挺奇怪的...为什 ...
- jQuery做字符串分隔
var str=new String(); var arr=new Array(); str="ddd,dsd,3,dd,g,k"; //可以用字符或字符串分割 arr=str.s ...
- HTTP协议请求方式: 中GET、POST和HEAD的介绍_孤帆一叶
HTTP协议中GET.POST和HEAD的介绍 2008-05-10 14:15 GET: 请求指定的页面信息,并返回实体主体.HEAD: 只请求页面的首部.POST: 请求服务器接受所指定的文档作为 ...
- 构造方法、 This关键字 、static、封装
1.1 构造方法 构造方法是一种特殊的方法,专门用于构造/实例化对象,形式: [修饰符] 类名(){ } 构造方法根据是否有参数分为无参构造和有参构. 1.1.1 无参构造 无参构造方法就是构造方法没 ...
- STM32 串口中断总结
原文:https://blog.csdn.net/weixin_42480952/article/details/82981409 最近在学习使用dma传输方式进行串口通讯,感觉这个很详细,存一下 . ...
- C++之语言概述
C++语言是广泛使用的程序设计语言之一,因其特有的优势在计算机应用领域占有重要一席. C语言的发展 20世纪70年代初,贝尔实验室的Dennis Richie 等人在B语言基础上开发出C语言,最初是作 ...