A:别急,咱们先来看一个简单的场景:html
人人网刷朋友新鲜事你应该用过吧?实现这样的功能有一种简单的方式,是让用户与服务器之间保持一个长轮询。html5
可是它与普通的 Ajax 不同,服务器不会立马返回信息,它会先 hold 住,等待应该返回信息了,它才会返回信息(好比你的好友发了一条新的状态)。node
从传统服务端来看(好比 Apache),每次一个新用户连到你的网站上,你的服务器得新开一个链接,每一个链接都须要占用一个线程,这些线程大部分都是闲着的(好比等你的好友发状态,查数据库等),虽然它们是闲着的,可是照样占用了内存,也就是说,若是用户达到必定的规模,服务器的内存就会耗光而瘫痪。web
解决办法有不少,好比说使用线程池,可是它依然是阻塞的,若是线程池里的全部线程都被阻塞(网速慢,被人恶意暂用)那么接下来的请求将会排队等待。数据库
Node.js 就不相同了,它使用了「非阻塞」与「事件驱动」模型,你能够把它想象成一个 Event Loop 循环,这个循环会一直跑。一个新的请求来了,Event Loop 接收这个请求,而后交给其余线程,好比查询数据库,而后响应一个 callback,接着接收其余请求,而不是等待数据库结果的返回。api
若是数据库返回告终果,服务端将会把它返回给客户端,并继续循环。这就是事件驱动:服务端只在有事情发生时,才会有相应的处理(或者是接受请求,或者是一些 callback)。promise
A:是的,简单来说,Node.js 的 Event Loop 是基于 libuv,而浏览器的 Event Loop 则是在 html5 规范 中定义,具体实现交给浏览器厂商。浏览器
A:对比来看,它们有点类似:服务器
在浏览器中比较简单,值得注意的一点是,会在每一个 tasks 以后,会把当前 microtask 队列里的任务都执行完:markdown
Node.js 稍微复杂一点,每次 Event Loop 都须要通过六个阶段,每个阶段以后,都会执行 nextTick、microtasks (resolved promise, 等):
┌───────────────────────┐ ┌─>│ timers │ <─── setTimeout/setInterval callback │ └──────────┬────────────┘ ┌─────────────────────────┐ │ │ │ nextTick queue │ │ │ <─────────────── │ │ │ │ │ microTask queue │ │ ┌──────────┴────────────┐ └─────────────────────────┘ │ │ I/O callbacks │ │ └──────────┬────────────┘ ┌─────────────────────────┐ │ │ │ nextTick queue │ │ │ <─────────────── │ │ │ │ │ microTask queue │ │ ┌──────────┴────────────┐ └─────────────────────────┘ │ │ idle, prepare │ <─── 仅内部使用 │ └──────────┬────────────┘ │ │ ┌─────────────────────────┐ │ │ │ nextTick queue │ │ │ <──────────────── │ │ │ │ │ microTask queue │ │ │ └─────────────────────────┘ │ │ ┌─────────────────────────┐ │ ┌──────────┴────────────┐ │ incoming: │ │ │ poll │ <────┤ connections, │ │ └──────────┬────────────┘ │ data, etc │ │ │ └─────────────────────────┘ │ │ ┌─────────────────────────┐ │ │ │ nextTick queue │ │ │ <──────────────── │ │ │ │ │ microTask queue │ │ │ └─────────────────────────┘ │ ┌──────────┴────────────┐ │ │ check │ <─── setImmediate callback │ └──────────┬────────────┘ ┌─────────────────────────┐ │ │ │ nextTick queue │ │ │ <─────────────── │ │ │ │ │ microTask queue │ │ ┌──────────┴────────────┐ └─────────────────────────┘ │ │ close callbacks │ <─── eg: socket.on("close",func) │ └──────────┬────────────┘ ┌─────────────────────────┐ │ │ │ nextTick queue │ │ │ <─────────────── │ │ └─────────────┴ │ microTask queue │ └─────────────────────────┘ 复制代码
来一段简单的代码,猜猜浏览器(Chrome)和 Node.js 分别输出什么:
console.log('start'); setTimeout(() => { console.log('timer1'); Promise.resolve().then(() => { console.log('promise1'); }); }, 0); setTimeout(() => { console.log('timer2'); Promise.resolve().then(() => { console.log('promise2'); }); }, 0); console.log('end'); 复制代码
A:咱们先来验证一下:
浏览器中:
start
end
timer1
promise1
timer2
promise2
复制代码
Node.js 中:
start
end
timer1
timer2
promise1
promise2
复制代码
看来和想象中的不同,别急,看个动图就会明白了:
浏览器中:
Node.js 中:
setTimeout callback
里加上 process.nextTick
那么是比 Promise.then
先执行?A:是的,还记得上面所说过的吗,在每一个阶段后都会执行 nextTick queue 以及 micktasks queue,nextTick queue 的优先级比 micktasks queue 高。
A:...
未完待续...
注:Node v11+ 版本后,运行的结果与浏览器中一致。