前言
上篇文章我们聊了事件循环机制在浏览器中的表现,本篇我们来聊聊宿主环境换成为Node.js时,会有什么变化。
初探
我们先以宏观视角看下在Node.js中Event Loop长什么样,话不多说,先上图:
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
关于这 6 个阶段,官网描述为:
- 定时器(timers):本阶段执行已经被
setTimeout()和setInterval()的调度回调函数。 - 待定回调(pending callbacks):执行延迟到下一个循环迭代的 I/O 回调。
- idle, prepare:仅系统内部使用。
- 轮询(poll):检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和
setImmediate()调度的之外),其余情况Node将在适当的时候在此阻塞。 - 检测(check):
setImmediate()回调函数在这里执行。 - 关闭的回调函数(close callbacks):一些关闭的回调函数,如:
socket.on('close', ...)。
setImmediate
这是一个在Node.js中实现但没有在浏览器中实现的API,我们先来看下它的定义:
该方法用来把一些需要长时间运行的操作放在一个回调函数里,在浏览器完成后面的其他语句后,就立刻执行这个回调函数。
该特性是非标准的,请尽量不要在生产环境中使用它!
看起来和setTimeout没什么区别,不过他们都运行在不同的阶段,setImmediate会在一旦在当前轮询阶段完成时,执行回调用。
我们来看一段比较坑爹的代码:
setTimeout(() => {
console.log('setTimeout');
}, 0);
setImmediate(() => {
console.log('setImmediate');
});
那你觉得是先输出setTimeout还是setImmediate呢?
其实我们在实现运行后发现,都有可能!!!
官方是这么说的:
- 执行计时器的顺序将根据调用它们的上下文而异。
- 如果两则都从主模块内调用,则计时器将受到进程性能的约束(这可能会受到计算机上其他正在运行应用程序的影响)。
- 如果你将这两个函数放入一个 I/O 循环内调用,
setImmediate总是被有限调用。
精简一下,大致意思就是说:它们的执行任务优先级不一样。
process.nextTick()
process.nextTick() 是一个异步任务,它属于微任务。
process.nextTick() 将 callback 添加到"next tick queue"。 在 JavaScript 堆栈上的当前操作运行完成之后,且在允许事件循环继续之前,此队列将被完全排空。 如果递归地调用 process.nextTick(),则可能会创建无限的循环。
小结
最后,我们来罗列下Node.js中的微、宏任务都有哪些:
- Macrotask:
setTimeout、setInterval、setImmediate、整体代码、I/O。 - 常见的 Microtask:
process.nextTick、Promise等。
在Node.js中,任务的调度分为了六个阶段,这点和浏览器略有不同。因为所处的阶段不同setTimeout和setImmediate的执行顺序也会因当前上下文环境而改变。
最后打波小广告,美团校招社招内推,不限部门,不限岗位,不限投递数量,海量hc,快来快来~