javascript - 如何利用JavaScript和Node.js中的事件循环?

标签 javascript node.js ecmascript-6 event-loop

事件循环的概念是通用的还是特定于语言的?我正在寻找带有示例的详细说明,以清楚地理解以下内容:

1.如何运作?


如何/何时/可以/如何利用JavaScript中的事件循环?
ECMAScript-6 / 2015中有关事件循环的任何更改?


更新:
对此并没有答案,但是我正在寻找一个带有示例的易于理解的定义。


附录:

考虑以下代码:

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        setTimeout( nextListItem, 0);
    }
};  


假设setTimeout()在这里使用事件循环来防止stackoverflow是否正确?我阅读了以下代码的解释:


  由于事件循环处理递归而不是调用堆栈,因此消除了堆栈溢出。


更新2

上面的代码中对setTimeout()的完整解释如下:


  由于事件循环处理递归而不是调用堆栈,因此消除了堆栈溢出。当nextListItem运行时,如果item不为null,则将超时函数(nextListItem)推送到事件队列中,并且该函数退出,从而使调用堆栈保持清晰。当事件队列运行其超时事件时,将处理下一个项目,并将计时器设置为再次调用nextListItem。因此,该方法从头到尾都进行了处理,而没有直接的递归调用,因此无论迭代次数如何,调用堆栈都保持清晰。


前面提到的解释和这里的答案中的矛盾,使得它变得更加难以理解。

最佳答案

事件循环的概念是通用的还是特定于语言的?


在最高级别,一般。但是不同的环境将以不同的方式实现细节,因此实际上您需要特定于环境的信息来了解正在发生的事情。


  怎么运行的?


所有这些细节都在规范中,主要是在Jobs and Job Queues部分中。

有几个关键方面:


有一系列作业(JavaScript规范的术语)或“任务”(如HTML规范所称)正在等待JavaScript引擎运行。引擎完成一项工作后,它将在队列中的下一个工作/任务(如果有)上工作。
作业一旦开始,它将一直运行直到完成;没有其他作业可以中断它。这称为“运行完成”,对于定义JavaScript的工作方式非常重要。
作业队列按顺序处理。


因此,请考虑以下代码:

console.log("one");
setTimeout(function() {
    console.log("two");
}, 1000);


如果您运行该代码(通过NodeJS或浏览器),则会发生以下情况(忽略一些不相关的细节):


一开始,队列为空,JavaScript引擎为空闲
环境(NodeJS,浏览器)将作业排队以运行脚本
JavaScript引擎开始工作并运行脚本:


输出“一个”
它为我们赋予setTimeout的匿名函数设置了一个计时器
工作结束

在某个时候,环境中的计时器机制确定是时候调用回调了,因此它将作业排队来调用它
JavaScript引擎从队列中提取该作业并运行该功能


输出“两个”
工作结束



现在考虑以下代码:

console.log(Date.now(), "one");
setTimeout(function first() {
    console.log(Date.now(), "two");
}, 500);
setTimeout(function second() {
    var end = Date.now() + 1000;
    while (end > Date.now()) {
        // Busy-wait (normally this is a Bad Thing™, I'm using it here
        // to simulate actual work that takes significant time
    }
}, 100);


如您所见,它在500ms安排了一个定时回调,然后在100ms安排了另一个。但是回调中100ms的代码至少需要1000ms才能运行。怎么了?


环境将作业排队以运行脚本
JS引擎负责这项工作


输出时间值和“ 1”,例如1470727293584 one
设置对函数first的定时回调,持续500ms
设置对函数second的定时回调,持续100ms
工作结束

大约100毫秒后,环境将作业排队以运行second
JavaScript引擎开始工作并运行功能


它开始工作(好吧,所以实际上只是忙于等待)

大约400毫秒(从设置计时器的时间起500毫秒)之后,环境将作业排队以调用first;由于JavaScript引擎正忙于上一个作业,因此该作业位于队列中
同时,JavaScript引擎仍在执行first的工作:


最终,由#3的JavaScript引擎完成的工作已经完成
工作完成

JavaScript引擎从队列中提取下一个作业,并运行对second的调用


它输出时间值和“两个”,例如1470727294687 two
工作结束



注意,环境在JavaScript忙时做了一些事情。它排队要完成的工作。

在JavaScript引擎繁忙时环境可以执行操作的事实非常重要。

(值得注意的是,在排队作业时,某些环境可能不一定将作业添加到队列的末尾;例如,在某些浏览器中,“队列”实际上是多个优先级略有不同的队列...)


  我如何/何时/可以利用它?


它并没有充分利用它,而是知道它在那里。同样重要的是要注意,尽管JavaScript具有“从运行到完成”的语义,但是即使JavaScript代码正在运行,运行JavaScript的环境也可能同时在做其他事情。


  ECMAScript-6 / 2015中有关事件循环的任何更改?


虽然在规范中比以前更完整地定义了它,但实际上不是。可能与更改最接近的是与承诺的关系:使用thencatch计划的回调将始终作为作业排队,而永远不会同步运行。这是JavaScript规范第一次定义异步发生的事情(ajax和计时器不是JavaScript规范的一部分)。



在下面,您已经问过:


  考虑以下代码:

var list = readHugeList();
var nextListItem = function() {
    var item = list.pop();
    if (item) {
        // process the list item...
        setTimeout(nextListItem, 0);
    }
};

  
  假设setTimeout()在这里使用事件循环来防止stackoverflow是否正确?


(我假设在那之后立即有一个nextListItem();调用。)

不,但是它在做其他重要的事情。该版本的非setTimeout版本如下所示:1

var list = readHugeList();
while (list.length) {
    var item = list.pop();
    // process the list item...
}


这是一个简单的循环,没有堆栈溢出的可能。

它正在做的事情是与事件循环协同工作,避免了长时间运行的工作会阻塞JavaScript引擎,从而阻止它处理任何其他工作(例如I / O完成,单击或其他事件)。因此,它通过一次处理一个项目将工作分解成一些小任务,有助于确保整个工作队列都得到继续处理。这意味着处理列表要花费更长的时间,但同时不会阻塞其他内容。



1确实,该代码的非setTimeout版本看起来像这样:

var list = readHugeList();
var nextListItem = function() {
    var item = list.pop();
    if (item) {
        // process the list item...
        // then recurse
        nextListItem();
    }
};
nextListItem();


...在那种情况下,可能会产生堆栈溢出,但这是编写该代码的非常奇怪的方式。

关于javascript - 如何利用JavaScript和Node.js中的事件循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38844135/

相关文章:

javascript - JS回文校验 "every"辅助方法分解

javascript - 如何在不影响所有键的情况下在 jquery 中使用 keydown 事件?

javascript - Node js/javascript : how to process streams sequentially with pipe and async/await when parsing csv and calling webservices with got?

javascript - yarn 升级后出现类型错误(ReactJS 项目)

java - Nodejs 性能事件循环

javascript - 使用惰性初始化将符号定义为属性

Javascript 在点击时更改表格内容

node.js - Angular 8 - GET styles.js net::ERR_CONTENT_LENGTH_MISMATCH 200 (OK) 当通过 USB 服务时

javascript - 如何在不重新启动应用程序的情况下清除所有用户的快速 session ?

javascript - 如何增加window.localStorage中限制的ELECTRON文件大小的大小限制?