前沿Node.js是一个基于V8引擎的javascript运行环境。Node.js具有事件驱动、非阻塞I/O等特点,结合NodeAPI,Node.js具有网络编程、文件系统等服务端功能,Node.js使用libuv库进行异步事件处理。线程Node.js单线程的意思其实就是指执行同步代码的主线程。一个Node程序的启动不仅分配了一个线程,而且我们只能在一个线程中执行代码。当有I/O资源调用、TCP连接等外部资源申请时,主线程不会阻塞,而是委托给I/O线程处理,进入Waiting队列。一旦主线程执行完毕,就会消耗事件队列(EventQueue)。因为主线程只有一个,只占用CPU核心处理逻辑计算,不适合CPU密集型使用。注意上图中的EVENT_QUEUE,给人的感觉好像只有一个队列。根据Node.js官方介绍,EventLoop有6个stage,每个stage都有对应的先进先出回调队列。什么是事件循环(EventLoop)?在计算机科学中,事件循环、消息调度器、消息循环、消息泵或运行循环是一种编程结构,它在程序中等待和调度事件或消息。--来自wiki关于意思:EventLoop是一种常用的机制,通过内部或外部的事件提供者发出请求,如文件读写、网络连接等异步操作,完成后调用事件处理器。整个过程就是异步阶段Node.js事件循环机制当Node.js启动时,会初始化事件循环,处理提供的输入脚本(或者落入REPL,本文档不涉及),可能会做异步API电话、日程计时器,或调用process.nextTick(),然后开始处理事件循环。--来自node.jsdoc大致意思:Node.js启动时,会初始化一个事件循环,处理脚本时可能会出现异步API行为调用,使用定时器任务或nexTick,处理完成后,进入事件循环处理流程事件循环阶段┌──────────────────────────────────────────────────────┐┌──>│定时器……──┐││I/O回调││└──────────────┬─────────────────────────────────────────────────────────────────────────────────────────────┘┌────────────────────────────────────────────────────————————┴────────────────┐││空闲,准备││└────────────┬────────────────────┘┌──────────────────┐│┌────────────┴───────────────┐┐传入:│││投票│<───────┤连接,││└──────────────┬───────────────┘│数据等││┌──────────────────────────────────────────┴────────────────────────────┘│检查│└────────────────────────────┘│┌────────────┴──────────────┐└──┤收盘回调│└────────────────────────────┘每个阶段都有一个FIFO回调队列,每个阶段都有自己的事件处理方法。当事件循环进入到某个阶段,就会在这个阶段执行回调,直到队列耗尽或者执行完最大回调次数,才会进入下一个处理阶段。timers阶段:这个阶段执行setTimeout(callback)和setInterval(callback)计划的回调;I/O回调阶段:执行关闭事件回调、定时器设置的回调(定时器、setTimeout、setInterval等)、setImmediate()回调以外的回调;(currentstage)idle,preparestage:仅供节点内部使用;poll阶段:获取新的I/O事件,节点在合适的情况下会阻塞在这里;检查阶段:执行setImmediate()设置的回调;closecallbacks阶段:比如socket.on('close',callback)的回调会在这个阶段执行。以下是creeperyang的节选。对于以上6个阶段(原文翻译)定时器阶段,一个定时器指定了一个下限时间,而不是确切的时间。达到这个最短时间后执行回调在指定的时间过去后,定时器会尽早执行回调,但系统调度或其他回调执行可能会延迟它们。注意:从技术上讲,轮询阶段控制计时器何时执行。注意:这个下限时间有一个范围:[1,2147483647],如果设置的时间不在这个范围内,则设置为1。I/O回调阶段这个阶段对一些系统操作进行回调。比如TCP错误,比如一个TCPsocket在尝试连接的时候收到ECONNREFUSED,类unix系统会等待报错,然后放入I/O回调阶段的队列中执行。该名称可能会误导为执行I/O回调处理程序,实际上I/O回调将由轮询阶段处理。轮询阶段轮询阶段有两个主要功能:执行达到最小时间的定时器的回调,然后处理轮询队列中的事件。当事件循环进入轮询阶段,并且没有设置定时器(没有调度定时器)时,会发生以下两种情况之一:如果轮询队列不为空,事件循环将遍历队列并执行同步回调,直到队列为空或执行的回调次数达到系统上限;如果轮询队列为空,将发生以下两种情况之一:如果代码已通过setImmediate()回调设置,事件循环将结束轮询阶段并进入检查阶段执行检查队列(在回调中).如果代码没有通过setImmediate()设置,事件循环会阻塞在这个阶段,等待回调被加入到轮询队列中,并立即执行。但是,当事件循环进入轮询阶段并设置了定时器后,一旦轮询队列为空(轮询阶段空闲状态):事件循环将检查定时器,如果一个或多个定时器的下限时间已到到达后,事件循环将绕回定时器阶段,并执行定时器队列。检查阶段此阶段允许在轮询阶段结束后立即执行回调。如果轮询阶段空闲并且有setImmediate()设置的回调,事件循环将进入检查阶段而不是等待。setImmediate()实际上是一个特殊的计时器,它在事件循环的一个单独阶段运行。它使用libuv的API将回调设置为在轮询阶段结束后立即执行。一般来说,随着代码的执行,事件循环最终会进入轮询阶段,在这个阶段等待传入的连接、请求等。但是只要有setImmediate()设置的回调,一旦poll阶段空闲,程序就会结束poll阶段进入check阶段,而不是继续等待poll事件(pollevents)。closecallbacks阶段如果一个socket或handle突然关闭(比如socket.destroy()),close事件会在这个阶段被触发,否则会被process.nextTick()触发SimpleEventLoopconstfs=require('fs');letcounts=0;functionwait(mstime){letdate=Date.now();while(Date.now()-date
