node.js 事件循环 异步IO 和 非IO异步

前些天略研究了一下node.js的异步原理,才对node 的处理异步机制有些了解,因而想着写篇博文,一是能够帮助正在学习的猿们,二是也能够巩固本身,若是本文有什么错误的地方,还请各位指出,我会加以改正。node

windows 的IOCP

首先我得先简单的说一下windows下处理高并发的异步模型,它是一种通讯模型,用来解决服务器高并发的一种技术,它就是IOCP(I/O Completion Port)IO完成端口。web

node异步IO

在node的底层libuv库里实现了IOCP这个异步模型,当咱们在windows下运行Node 的程序,对于异步的请求处理,内部会用线程池来实现,这个线程池会被windows 管理,而这种实现方式就是IOCP的实现方式。windows

说到异步IO咱们得先看一下是事件循环,它是整个异步实现的核心,它不断的从观察者把事件取出,分离回调来执行。
这里写图片描述promise

整个绿色的区域就是一个事件循环,它的做用就在于:
①取出事件
②执行相关联的回调bash

若是咱们要执行一次异步IO,那它的整个流程是怎样的呢,好比我要执行一次读取文件的操做,这里我用promise来实现。服务器

const fs = require("fs");
let readFile = path => {
    return new Promise((resolve,reject) => { fs.readFile(path,"utf-8",(err,data) => { if(err){ reject(err); } else{ resolve(data); } }); }); }; readFile("./data.txt") .then(data => { console.log(data); }) .catch(err => { console.log("err"); });

那么它大概经历了如下的几个流程
这里写图片描述并发

能够看出,node的异步请求处理其实是由线程池来支持的,所以实际上的处理过程仍是在工做线程,只是这个线程不对用户开放,而是由系统来接手管理。异步

非IO异步

如今来看一下非IO异步操做,好比说setTimeout(),setInterval(),setImmdiate()和process.nextTick(),前面文章我已经说过nextTick比setTimeout要快的多,那么为何呢?svg

咱们能够先来验证这个结论函数

process.nextTick(() => { console.log("nextTick done"); }); setTimeout(() => { console.log("setTimeout done"); },0);

输出:

nextTick done
setTimeout done

若是咱们将两个执行顺序对调一下,咱们会发现结果仍是同样的。

setTimeout(() => { console.log("setTimeout done"); },0); process.nextTick(() => { console.log("nextTick done"); }); 

输出:

nextTick done
setTimeout done

可是为何nextTick 的速度要比setTimeout快的多呢。
这是由于setTimeout或者setInterval建立的定时器会被插入到观察者内部的一个红黑树中,可是setTimeout 或 setInterval的定时器都是不精确的,假如咱们延迟了10s,但在9s的时候忽然被一个延时操做占用了8秒,那么此次延时就不仅10s了。

有些时候咱们仅仅是想让一个函数异步执行,而不是想让它延迟多少秒后执行,这时候咱们能够用process.nextTick(),由于相对于setTimeout来讲,process.nextTick()更加轻量,不用花费去建立一个红黑树那么大的开销,它的内部是由一个队列直接来支持的,所以比setTimeout要快不少。

下面咱们看一下setImmediate() 这个函数也是让传入的回调函数延迟执行,和process.nextTick()函数的做用相同,可是它们仍是有实际的差别的,好比咱们运行下面的代码:

setImmediate(() => { console.log("setImmediate done"); }); process.nextTick(() => { console.log("nextTick done"); });

输出 :

nextTick done
setImmediate done

在调换书写顺序时,结果仍是同样的

process.nextTick(() => { console.log("nextTick done"); }); setImmediate(() => { console.log("setImmediate done"); });

输出 :

nextTick done
setImmediate done

从这里能够看出process.nextTick也要比setImmediate快,这是由于process.nextTick 属于 idle 观察者,而setImmediate属于 check观察者,从优先级来看,idle 观察者要 > check观察者 > IO观察者