当前位置: 首页 > 后端技术 > Node.js

Node学习笔记:Nodebasics&eventloop

时间:2023-04-03 16:37:40 Node.js

[TOC]Nodebasics1.NodeoverviewNodeAPI,javascriptutilityNodeCore,一套用于实现NodeAPI的javascript模块,基于libuv,V8引擎等javacript Engine,将js语言翻译成机器码,node使用V8引擎翻译所有js代码,但node不一定使用V8作为js引擎,也可以使用其他js引擎。EventLoop,事件循环,Node使用libuv实现事件循环,主线阶段和事件循环阶段都在一个线程中完成。要实现事件循环,需要使用节点异步API,并使用回调函数作为参数。这些回调函数将在事件周期内运行。Node不仅适合I/O密集型工作,同时CPU密集型工作也能胜任。libuv使用称为“工作池”的线程池,即用于卸载I/O密集型任务和CPU密集型任务的线程池。2.Node事件循环1.1非阻塞I/ONode使用三种技术来阻止阻塞业务逻辑的执行,即事件、异步API和非阻塞I/O。非阻塞I/O是指当程序在做其他事情时,程序可以发起获取网络资源的请求,并在成功获取网络资源后,执行回调函数处理网络资源;1.2Node的事件循环来自IBM的AtutorialonNODE。首先,让我们简要解释一下什么是事件循环(或事件轮询)。很容易联想到组合原理课上关于I/O访问的章节。最原始的I/O访问方式是程序循环,即处理器每隔一段时间访问每个I/O端口,检查每个端口是否就绪。事件轮询简单理解就是一个单向运行的先进先出队列。在js主线完成Node生命周期后,Node线程会开始执行事件循环,即执行Node非阻塞API中的回调函数。如果代码中没有非阻塞代码,运行完主线后,节点结束;如果有非阻塞的Blocking代码,所有回调函数执行完后,节点结束。一些概念性的任务队列(TaskQueue)上图中的每个阶段都会有一个FIFO任务队列。一般来说,特定于某个阶段的逻辑会在最开始执行,直到队列为空或达到系统的限制。需要注意的是,TaskQueue并不是一个真正的Queue,而是一个集合,它选择第一个可执行的Task来执行,并不是严格意义上的出队和入js的异步任务,网上分为Hongtasks和Microtasks。微任务(Microtask)会在主线和事件的每个阶段完成后立即执行微任务回调。Node开发者编写的代码只是以微任务的形式存在。在主线上运行,在Timers阶段,在Poll阶段,在Check阶段。一般的看法是,promise回调是微任务,但在某些浏览器(MicrosoftEdge、Firefox40、iOSSafari和桌面Safari8.0.8)中,promise回调被视为一个新的宏任务,而不是微任务。主要包括:Promise.then、MutaionObserver、process.nextTick(Node.js环境)-宏任务(Task)
主要包括:script(全局任务)、setTimeout、setInterval、I/O、UI交互事件、postMessage、requestAnimationFrame,MessageChannel,setImmediate(Node.js环境)主要阶段说明一般情况下,事件循环由多个调用回调函数阶段组成定时器阶段:会运行setInterval()和setTimeout()过期定时器回调函数轮询阶段:操作系统会轮询所有I/O操作是否完成,如果完成则运行相应的回调函数。检查阶段:setImmediate()回调函数将运行。详细阶段说明根据上图,在节点时间循环中,更详细的事件阶段是:Timers阶段任何过期的定时器回调都会在事件循环的这个阶段运行,定时器分为两种setImmediate()立即定时器是一个节点对象,它在下一个检查阶段立即运行setTimeout(),Timeout定时器也是一个节点对象,一旦定时器超时就运行回调,并且在定时器到期后,在下一个Timers阶段运行回调事件循环。Timeout定时器有两种类型,分别是:Interval,由setInterval()创建。每次计时器到期时,都会运行回调。只要节点进程还在运行,这个过程就会重复,除非你调用ClearInterval()Timeout定时器是由setTimeout()创建的。超过设置的延迟(默认1毫秒)事件后,将运行回调,除非在回调运行之前调用clearTimeout()。当没有过期的定时器回调要运行时,事件循环将运行所有的微任务。运行微任务后,事件循环进入Pending阶段-Pending阶段
一些系统的回调发生在这个阶段-Idle和Prepare阶段-Poll阶段
这个阶段执行I/O回调,如果轮询队列为空(除了I/O之外没有其他事件),它会阻塞并等待任何正在进行的I/O操作完成,然后立即执行这些操作的回调。如果定时器被调度,Poll阶段将结束,必要时运行微任务,然后事件循环进入Check阶段-Checkphase
只有`setImmediate()`回调会在这个阶段被执行,允许用户当轮询阶段空闲时立即执行一些代码。Check阶段的回调队列为空后,会运行所有的microtasks,然后事件循环会进入Close阶段-Close阶段
如果一个套接字(socket)或句柄突然关闭(例如,如果套接字的`socket.destroy()`方法被调用),在这种情况下它的关闭事件被触发。通过代码加深理解constfs=require('fs');const{setImmediate}=require('timers');varcnt=0;constevent=()=>{if(cnt>=1){clearInterval(myInterval)}console.log(`${cnt}:intervalbegin`)fs.readFile('../.images/node-structure.png',(err,file)=>{console.log(`${cnt}:pollbegin`)if(err){console.log(err)}else{console.log(file.length)}})setImmediate(()=>{console.log(`${cnt}:immediate`)})cnt++;}constmyInterval=setInterval(event,1);运行结果0:intervalbegin1:immediate1:intervalbegin2:immediate2:pollbegin1904282:pollbegin190428其中interval在1ms回调,在第一个循环中,先ExecuteTimers阶段,此时cnt=1,interval到期,执行回调函数,先输出intervalbegin,然后进入pending、idle、prepare阶段,然后开始poll进行I/O访问。此时I/O还没有准备好,到达check阶段,执行immediat,然后开始第二次循环。此时cnt=1,先执行clearInterval函数,然后cnt++,Timers阶段后,cnt=2。第二次循环后,I/O还没有准备好,所以执行立即函数,第二次循环完成后,回调队列中只有I/O回调没有完成,所以阻塞在第三个周期,直到I/O事件完成。参考IBMnode.js漫游《Node.js实战(第二版)》JS中的EventLoop和Taskqueueh生活水平