Code前端首页关于Code前端联系我们

关注 大佬 - Node 中的 eventloop

terry 2年前 (2023-09-08) 阅读数 140 #Vue

废话不多说,我们继续看书吧。

事件循环和

节点

Node也是单线程的,在处理eventloop上与浏览器略有不同。从API层面来说,Node增加了两个方法process.nextTicksetImmediate

上面我们提到了,node中的事件循环是一个基于libuv的库,那么这个库的实现机制是怎样的呢?

跟着大佬走——node中的eventloop跟着大佬走——node中的eventloop

官网对此的解释是:

  • 定时器(Timer):该阶段执行setTimeout()setInterval()执行的索赔功能。
  • 挂起的回调:其执行被推迟到下一次循环迭代的 I/O 回调。
  • 空闲,准备:仅由系统内部使用。
  • poll:检索新的I/O事件;执行I/O相关的回调(几乎所有情况下,除了关闭回调,这些回调是由setImmediate()中的定时器调度的),其他情况下节点会在适当的时间阻塞在这里。
  • check(检查):setImmediate()这里执行call函数。
  • 关闭回调(close callbacks):一些关闭回调,如:socket.on('close', …)。

设置超时和设置立即

显示代码:

setTimeout(() => {
  console.log('timeout');
}, 0);

setImmediate(() => {
  console.log('immediate');
});
 

节点环境下运行结果为:

timeout
immediate
 

immediate
timeout
 

没想到,我们来看看官方的说法:

首先,定时器的执行顺序将根据调用它们的上下文而变化。

其次,当从主模块调用时, 进程 的时序是有限的。

如果在 I/O 循环中,则 setImmediate 始终被调用有限次。

借用博主的话来解释一下:

使用setImmediate相对于setTimeout的优点是,无论存在多少个定时器,setImmediate总是在IO周期中的每个定时器之前执行。

process.nextTick()

此 API 类似于昨天文章中的 PromiseMutationObserver 微任务实现。您始终可以将 ​​ 放入代码块中,以确保 在下一个宏任务开始之前执行。

显示代码:

class Lib extends require('events').EventEmitter {
  constructor () {
    super()

    this.emit('init')
  }
}

const lib = new Lib()

lib.on('init', _ => {
  console.log('init!')
})
 

上面的代码片段显示控制台从未被执行。我们来看看如何解决process.nextTick的输出问题:

class Lib extends require('events').EventEmitter {
  constructor () {
    super()

    process.nextTick(_ => {
      this.emit('init')
    })
  }
}
 

不容易啊!当节点主线程执行完毕后,程序触发eventloop流程寻找错误的微任务,如果微任务队列不为空,则发送init事件。

浏览器可以使用Promise.resolve()来达到同样的效果。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门