node的事件循环与js的区别,nodejs中的事件循环的执行顺序

  node的事件循环与js的区别,nodejs中的事件循环的执行顺序

  什么是事件周期?本文介绍了Node中的事件循环,希望对你有所帮助!

  node.js速度课程简介:进入学习

  

什么是事件循环?

  虽然JavaScript是单线程的,但是事件循环尽可能使用系统内核,允许Node.js执行非阻塞I/O操作。

  尽管大多数现代内核都是多线程的,但它们可以在后台处理多线程任务。当一个任务完成后,内核告诉Node.js,然后适当的回调将被添加到循环中执行。本文将进一步详细介绍这个话题。

  

时间循环解释

   node . js开始执行时,会先初始化事件循环,处理提供的输入脚本(或者放入REPL,本文档不涉及)。这将执行异步API调用,调度定时器,或者调用process.nextTick(),然后开始处理事件循环。

  下图显示了事件循环执行顺序的简单概述。

  计时器

   待定的回调

   闲着,准备

  来袭:

   民意测验的人脉,

  数据等。

   检查

  关闭回调

  每一个盒子代表着事件循环的一个阶段

  每个阶段都有一个FIFO队列回调,但是每个阶段都以自己的方式执行。一般来说,当事件循环进入一个阶段时,它会执行当前阶段的任何操作,并开始执行当前阶段队列中的回调,直到队列被完全消耗或队列中的最大数据被执行完。当队列用尽或达到最大数量时,事件循环将移动到下一阶段。

  

阶段概述

  timers这个阶段执行setTimeout()和setInterval()的回调pending callbacks。I/O回调被推迟到下一个循环迭代idle,prepare。使用poll仅在内部检索新的I/O事件。执行I/O相关回调(几乎所有相关回调,关闭回调,)checksetImmediate()会调用close callbacks在这个阶段关闭回调,例如:socket.on(close ,).在事件循环的每个过程中,Node.js检查它是否正在等待异步I/O和计时器

  

阶段详情

  

timer

  计时器指定回调将被执行的临界点,而不是人们希望它被执行的时间。计时器将在指定的过去时间后尽快执行。但是,操作系统调度或其他回调会延迟它的执行。

  从技术上讲,轮询阶段决定了何时执行回调。

  例如,您设置了一个计时器,它将在100毫秒后执行,但是您的脚本异步读取一个文件需要95毫秒。

  const fs=require( fs );

  函数someAsyncOperation(回调){

  //假设这需要95毫秒才能完成

  fs.readFile(/path/to/file ,回调);

  }

  const time out scheduled=date . now();

  setTimeout(()={

  const delay=date . now()-time out scheduled;

  console . log(` { delay }毫秒后,我已被安排`);

  }, 100);

  //执行需要95毫秒才能完成的someAsyncOperation

  someasyncooperation(()={

  const start callback=date . now();

  //做一些需要10毫秒的事情.

  while(date . now()-start callback 10){

  //什么都不做

  }

  });当事件循环进入轮询阶段时,它是一个空队列,(fs.readFile()尚未完成),因此它将等待剩余的毫秒,直到达到最快的计时器阈值。95 ms后,fs.readFile()完成读取文件,完成将其添加到轮询阶段并执行它需要10 ms。当回调完成时,队列中没有要执行的回调,事件循环返回到计时器阶段。在本例中,您将看到定时器在执行前延迟了105 ms。

  为了防止轮询阶段阻塞事件循环,libuv(实现事件循环和平台上所有异步行为的C语言库)也在轮询阶段设置了一个最大值,以停止轮换训练更多的事件。

  

pending callbacks

  此阶段执行某些系统操作的回调,如TCP错误类型。例如,如果TCP socket在尝试连接时收到ECONNREFUSED,某些*nix系统希望等待报告错误。这将在挂起的回调阶段排队等待执行。

  

poll

  轮询阶段有两个主要功能

  计算I/O阻塞的时间,并执行轮询队列中的事件。当事件循环进入轮询阶段并且没有计时器时,会发生以下两件事

  如果轮询队列不为空,事件循环将同步迭代执行每个回调,直到执行完所有回调,或者达到系统的硬限制。如果轮询队列为空,将会发生以下两种情况。如果是setImmediate的回调,事件循环将结束轮询阶段,进入检查阶段执行回调。如果不是setImmediate,事件循环将等待回调被添加到队列中,然后立即执行。一旦轮询队列为空,事件循环将检测计时器是否到了,如果是,事件循环将到达计时器阶段以执行计时器回调。

  

check

  此阶段允许人们在轮询阶段完成后立即执行回调。如果轮询阶段变得空闲,并且脚本已经用setImmediate()排队,则事件循环可能会继续到检查阶段,而不是等待。

  SetImmediate()实际上是一个特殊的计时器,在事件周期的一个单独阶段运行。它使用libuv API来调度在轮询阶段完成后执行的回调。

  通常,随着代码的执行,事件循环将最终到达轮询阶段,该阶段将等待传入的连接、请求等。但是,如果使用setImmediate()调度回调,并且轮询阶段变得空闲,它将结束并继续检查阶段,而不是等待轮询事件。

  

close callbacks

  如果一个套接字或操作突然关闭(如socket.destroy()),关闭事件将被发送到此阶段,否则将通过process.nextTick()发送。

  

setImmediate() VS setTimeout()

   setImmediate()和setTimeout()类似,但不同的行为取决于何时调用它们。

  一旦轮询阶段结束,SetTimmediate()将被执行。setTimeout()将在短时间后执行。每个回调的执行顺序取决于调用它们的上下文。如果同时调用同一个模块,时间将受到进程性能的限制(这也会受到运行在这台机器上的其他应用程序的影响)

  例如,如果我们不在I/O中运行以下脚本,尽管它受到进程性能的影响,但我们无法确定这两个计时器的执行顺序:

  //timeout_vs_immediate.js

  setTimeout(()={

  console.log(timeout )。

  }, 0);

  setImmediate(()={

  console . log(“immediate”);

  });$ node timeout_vs_immediate.js

  超时

  马上

  $ node timeout_vs_immediate.js

  马上

  但是,如果您进入I/O循环,立即回调将总是首先执行

  //timeout_vs_immediate.js

  const fs=require( fs );

  fs.readFile(__filename,()={

  setTimeout(()={

  console.log(timeout )。

  }, 0);

  setImmediate(()={

  console . log(“immediate”);

  });

  });$ node timeout_vs_immediate.js

  马上

  超时

  $ node timeout_vs_immediate.js

  马上

  TimeTimeTimeImmediate相对于setTimeout的优势在于,timeoutsetImmediate总是在I/O中的任何计时器之前执行,而不管存在多少个计时器。

  

process.nextTick()

  虽然process.nextTick()是异步API的一部分,但您可能已经注意到它没有出现在图中。这是因为process.nextTick()不是事件循环技术的一部分。相反,nextTickQueue将在当前操作完成后执行,而不考虑事件循环的当前阶段。这里,操作被定义为来自底层C/C处理器的转换,并处理需要执行的JavaScript。

  根据图表,可以在任何阶段调用process.nextTick()。在事件循环继续执行之前,将执行传递给process.nextTick()的所有回调。这将导致一些不好的情况,因为它允许您递归地调用process . nexttick()‘饿死’您的I/O,这将阻止事件循环进入轮询阶段。

  

为什么这会被允许

  为什么Node.js会收录这种情况?因为Node.js的设计思想是一个API应该总是异步的,即使它不是必须的,看看下面的片段。

  函数apiCall(arg,callback) {

  if (typeof arg!==string )

  返回process.nextTick(

  回拨,

  new TypeError(“参数应为字符串”)

  );

  }会对片段进行参数检查,如果不正确,会将错误传递给回调。API最近进行了更新,允许将参数传递给process.nextTick(),这允许它接受回调后传递的任何参数作为回调的参数进行传播,因此您不必嵌套函数。

  我们所做的是将错误返回给用户,但前提是我们允许用户代码的其余部分执行。使用process.nextTick(),我们保证apiCall()总是在剩余的用户代码之后、允许事件循环继续之前运行其回调。为了实现这一点,允许JS调用栈扩展,然后立即执行提供的回调。这允许人们在不到达RangeError的情况下对process.nextTick()进行递归调用:从v8开始超过了最大调用堆栈大小。

  更多关于node的信息,请访问:nodejs教程!即上述事件周期是什么?解释Node.js中事件循环的细节,更多请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: