资讯专栏INFORMATION COLUMN

node的事件机制

Faremax / 2870人阅读

摘要:什么是事件循环尽管是单线程的,事件循环机制,通过在合适的时候把操作交给系统内核,从而允许执行非阻塞的操作当操作完成时,内核告知,合适的回调函数会被加入轮询队列,最终被执行。结果长时间的运行回调函数允许运行事件比的阈值更长。

什么是事件循环(event loop)?

尽管js是单线程的,事件循环机制,通过在合适的时候把操作交给系统内核,从而允许node执行非阻塞的io操作
当操作完成时,内核告知node.js,合适的回调函数会被加入轮询队列,最终被执行。
Node.js启动的时候,初始化event loop,处理提供的脚本,脚本中可能调用异步API,调度timers,或者调用process.nextTick(),然后处理event loop

下图是简化的事件循环操作顺序图overview

     ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
      └───────────────────────┘

图中每个box就是一个phase,每个phase有一个先进先出的回调函数的队列,
event loop进入了一个phase,就会执行phase中所有的操作,然后执行回调函数,直到队列耗尽了,或者回调函数执行数量到达最大数,接下来就去下一个phase

因为任何一个操作都可能调度更多的操作,而且poll phase中新的事件由内核排队,所以正在轮询的事件在被处理的时候,poll事件们可能会排队。
结果:长时间的运行回调函数允许poll phase运行事件比timer的阈值更长。

phase overview 阶段概况

timers:执行由setTimeout() and setInterval()调度的回调函数

I/O callbacks:执行所有的回调函数,除了 close callbacks(由timers,setImmediate()调度)

idle, prepare:内部使用

poll:获取新的io事件,当合适的时候,node会阻塞在这里

check: setImmediate()回调函数会在这里调用

close callbacks: e.g. socket.on("close", ...)

每次运行event loop,node检查是否有对任何异步io或者timers的等待,没有就关闭

Phases in Detail(各阶段细述) timers

timers指定阈值(threshold)之后,会执行回调函数,但threshold不是执行回调函数的确切时间(只是最短时间)。
timers回调函数一旦可以执行了就会被执行。然而操作系统的调度或者其他的回调函数可能推迟它的执行。
由poll phase来控制什么时候timers被执行

var fs = require("fs");
function someAsyncOperation (callback) {
  // Assume this takes 95ms to complete
  fs.readFile("/path/to/file", callback);
}
var timeoutScheduled = Date.now();
setTimeout(function () {
  var delay = Date.now() - timeoutScheduled;
  console.log(delay + "ms have passed since I was scheduled");
}, 100);
// do someAsyncOperation which takes 95 ms to complete
someAsyncOperation(function () {
  var startCallback = Date.now();
  // do something that will take 10ms...
  while (Date.now() - startCallback < 10) {
    ; // do nothing
  }
});

一开始timer被调度,里面的回调函数执行log。
然后事件循环进入poll phase,此时队列是空的(因为fs.readFile()没有完成),所以就会等着,直到最早的timer的阈值(100)到时间,等了95 ms(还没到,毕竟定的是100),fs.readFile() 这个时候完成了,所以它的回调函数就回被加poll的队列并且被执行(执行10s),当回调函数完成了,队列又空了,所以,event loop将会看到timer的阈值(100)已经到了,
然后回到timers这个phase去执行timers的回调函数,也就是,打印出105秒

为了防止poll phase 独占耗尽 event loop,libuv 也有一个最大值(基于系统),会在超过最大值之前停止轮询更多的事件。

I/O callbacks

为系统操作(比如tcp错误类型)执行回调函数
当tcp socket尝试连接时接收到ECONNREFUSED,类unix系统将会想报道错误,要会在这个phase排队执行。

poll

poll phase有两个功能

为到了时间的timers执行脚本,然后

处理poll队列的事件

当event loop 进入poll phase且没有timers被调度,下面的事情会发生

poll不空,

通过回调函数队列迭代的执行

poll栈是空的

如果脚本已经被setImmediate()调度,事件循环将会终止poll phase,到check phase去执行那些被调度的脚本

等着回调函数被加进队列,然后立马执行它
一旦poll空了,event loop将回检查timers有没有thresholds到了,有的话,wrap back to the timers phase,然后执行timers的回调函数

check

特别的 timer

close callbacks setImmediate and setTimeout()

在poll完成以后执行

在最小事件之后执行

执行顺序:
依赖于调用的上下文

如果都在main module ,事件会被进程的性能限制(被其他应用影响)

not within an I/O cycle:不确定的

within an I/O cycle:immediate总是先(更好)

// timeout_vs_immediate.js
setTimeout(function timeout () {
  console.log("timeout");
},0);

setImmediate(function immediate () {
  console.log("immediate");
});
// timeout_vs_immediate.js
var fs = require("fs")

fs.readFile(__filename, () => {
  setTimeout(() => {
    console.log("timeout")
  }, 0)
  setImmediate(() => {
    console.log("immediate")
  })
})

The Node.js Event Loop, Timers

参考:

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81744.html

相关文章

  • Node.js】理解事件循环机制

    摘要:前沿是基于引擎的运行环境具有事件驱动非阻塞等特点结合具有网络编程文件系统等服务端的功能用库进行异步事件处理线程的单线程含义实际上说的是执行同步代码的主线程一个程序的启动不止是分配了一个线程,而是我们只能在一个线程执行代码当出现资源调用连接等 前沿 Node.js 是基于V8引擎的javascript运行环境. Node.js具有事件驱动, 非阻塞I/O等特点. 结合Node API, ...

    Riddler 评论0 收藏0
  • JS与Node.js中事件循环

    摘要:的单线程,与它的用途有关。特点的显著特点异步机制事件驱动。队列的读取轮询线程,事件的消费者,的主角。它将不同的任务分配给不同的线程,形成一个事件循环,以异步的方式将任务的执行结果返回给引擎。 这两天跟同事同事讨论遇到的一个问题,js中的event loop,引出了chrome与node中运行具有setTimeout和Promise的程序时候执行结果不一样的问题,从而引出了Nodejs的...

    abson 评论0 收藏0
  • node核心特性理解

    摘要:概述本文主要介绍了我对的一些核心特性的理解,包括架构特点机制核心模块与简单应用。在此期间,主线程继续执行其他任务。延续了浏览器端单线程,只用一个主线程执行,不断循环遍历事件队列,执行事件。 原文地址在我的博客,转载请注明来源,谢谢! node是在前端领域经常看到的词。node对于前端的重要性已经不言而喻,掌握node也是作为合格的前端工程师一项基本功了。知道node、知道后端的一些东西...

    huangjinnan 评论0 收藏0
  • 初窥JavaScript事件机制实现(一)—— Node.js事件驱动实现概览

    摘要:如果当前没有事件也没有定时器事件,则返回。相关资料关于的架构及设计思路的事件讨论了使用线程池异步运行代码。下一篇初窥事件机制的实现二中定时器的实现 在浏览器中,事件作为一个极为重要的机制,给予JavaScript响应用户操作与DOM变化的能力;在Node.js中,事件驱动模型则是其高并发能力的基础。 学习JavaScript也需要了解它的运行平台,为了更好的理解JavaScript的事...

    lavor 评论0 收藏0
  • 来,告诉你Node.js究竟是什么?

    摘要:当某个执行完毕时,将以事件的形式通知执行操作的线程,线程执行这个事件的回调函数。为了处理异步,线程必须有事件循环,不断的检查有没有未处理的事件,依次予以处理。另外,单线程带来的好处,操作系统完全不再有线程创建销毁的时间开销。 前言 如果你有一定的前端基础,比如 HTML、CSS、JavaScript、jQuery;那么,Node.js 能让你以最低的成本快速过渡成为一个全栈工程师(我称...

    Jonathan Shieber 评论0 收藏0
  • Node - 异步IO和事件循环

    摘要:它是在的基础上改进的一种方案,通过对文件描述符上的事件状态进行判断。检索新的事件执行与相关的回调几乎所有情况下,除了关闭的回调函数,它们由计时器和排定的之外,其余情况将在此处阻塞。执行事件的,例如或者。 前言 学习Node就绕不开异步IO, 异步IO又与事件循环息息相关, 而关于这一块一直没有仔细去了解整理过, 刚好最近在做项目的时候, 有了一些思考就记录了下来, 希望能尽量将这一块的...

    MyFaith 评论0 收藏0

发表评论

0条评论

Faremax

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<