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

如何利用JavaScript和Node.js中的事件循环?,javascript,node.js,ecmascript-6,event-loop,Javascript,Node.js,Ecmascript 6,Event Loop,事件循环的概念是通用的还是特定于语言的?我希望通过示例进行详细解释,以清楚地理解以下内容: 一,。它是如何工作的 如何/何时/可以利用JavaScript中的事件循环? ECMAScript-6/2015中引入的关于事件循环的任何更改? 更新: 这方面的答案并不缺乏,但我正在寻找一个简单易懂的定义,并举例说明。 增编: 考虑以下代码: var list = readHugeList(); var nextListItem = function() { var item = list.

事件循环的概念是通用的还是特定于语言的?我希望通过示例进行详细解释,以清楚地理解以下内容:

一,。它是如何工作的

如何/何时/可以利用JavaScript中的事件循环? ECMAScript-6/2015中引入的关于事件循环的任何更改? 更新: 这方面的答案并不缺乏,但我正在寻找一个简单易懂的定义,并举例说明。

增编: 考虑以下代码:

var list = readHugeList();

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

    if (item) {
        // process the list item...
        setTimeout( nextListItem, 0);
    }
};  
这里假设setTimeout使用事件循环来防止堆栈溢出是否正确?我阅读了对该代码的解释:

堆栈溢出被消除,因为事件循环处理递归,而不是调用堆栈

更新2 上面代码中对setTimeout的完整解释如下:

堆栈溢出被消除,因为事件循环处理递归,而不是调用堆栈。运行nextListItem时,如果项不为null,则超时函数nextListItem将被推送到事件队列,并且该函数将退出,从而使调用堆栈保持清除状态。当事件队列运行其超时事件时,将处理下一项,并将计时器设置为再次调用nextListItem。因此,该方法从头到尾都在处理,不需要直接递归调用,因此无论迭代次数多少,调用堆栈都保持清晰

前面提到的解释和这里的答案之间的矛盾使它更加难以理解

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

在最高级别,将军。但是不同的环境将以如此不同的方式实现细节,以至于您确实需要特定于环境的信息来了解发生了什么

它是如何工作的

所有血淋淋的细节都在说明书中,主要是在名为

有几个关键方面:

当HTML规范调用作业时,JavaScript规范的术语或任务中有一个作业队列等待JavaScript引擎运行。当引擎完成一个作业时,它将开始处理队列中的下一个作业/任务(如果有)

一旦作业开始,它就会一直运行,直到完成为止;没有其他工作可以打断它。这称为从运行到完成,对于定义JavaScript的工作方式非常重要

作业队列按顺序处理

请考虑这个代码:

console.log("one");
setTimeout(function() {
    console.log("two");
}, 1000);
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);
var list = readHugeList();
var nextListItem = function() {
    var item = list.pop();
    if (item) {
        // process the list item...
        setTimeout(nextListItem, 0);
    }
};
如果您通过NodeJS或浏览器运行该代码,忽略一些不相关的细节会发生以下情况:

开始时,队列为空,JavaScript引擎处于空闲状态 环境节点,浏览器将作业排队以运行脚本 JavaScript引擎将拾取作业并运行脚本: 它输出一个 它为我们提供给setTimeout的匿名函数设置了一个计时器 工作结束了 在某个时刻,环境中的计时器机制确定是时候调用回调了,因此它将作业排队以调用回调 JavaScript引擎从队列中获取该作业并运行该函数 它输出两个 工作结束了

现在考虑这个代码:

console.log("one");
setTimeout(function() {
    console.log("two");
}, 1000);
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);
var list = readHugeList();
var nextListItem = function() {
    var item = list.pop();
    if (item) {
        // process the list item...
        setTimeout(nextListItem, 0);
    }
};
如您所见,它将定时回调安排为500毫秒,然后再安排一次100毫秒。但是100毫秒回调中的代码至少需要1000毫秒才能运行。会发生什么

环境将作业排队以运行脚本 JS引擎将执行该任务 输出时间值和一,例如1470727293584一 在将来的500毫秒内,首先设置对函数的定时回调 在未来的100毫秒内设置对函数秒的定时回调 工作结束了 大约100毫秒后,环境将作业排队等待第二次运行 JavaScript引擎接收作业并运行函数 它开始正常工作了,所以真的只是忙着等待 大约400毫秒后,在设置计时器500毫秒后,环境将作业排队等待首先调用;由于JavaScript引擎忙于上一个作业,因此该作业位于队列中 与此同时,JavaScript引擎仍在处理第一个作业调用: 最终,JavaScript引擎从3开始所做的工作完成了 工作结束了 JavaScript引擎从队列中拾取下一个作业并运行对second的调用 它输出时间值和2,例如1470727294687 2 工作结束了 注意,当JavaScript繁忙时,环境做了一些事情;它把要做的工作排成队列

当JavaScript引擎繁忙时,环境可以做一些事情,这一点非常重要

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

我应该如何/何时/可以利用它

与其说是利用它,不如说是知道它在那里。还需要注意的是,尽管JavaScript具有从运行到完成的语义,但它运行的环境可能同时在做其他事情,甚至 当JavaScript代码运行时

ECMAScript-6/2015中引入的关于事件循环的任何更改

不完全是这样,尽管它在规范中的定义比以前更加完整。最接近更改的可能是与承诺有关:您使用then或catch计划的回调将始终作为作业排队,它永远不会同步运行。这是JavaScript规范第一次定义异步发生的事情,ajax和计时器不属于JavaScript规范的一部分

下面是您的提问:

考虑以下代码:

console.log("one");
setTimeout(function() {
    console.log("two");
}, 1000);
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);
var list = readHugeList();
var nextListItem = function() {
    var item = list.pop();
    if (item) {
        // process the list item...
        setTimeout(nextListItem, 0);
    }
};
这里假设setTimeout使用事件循环来防止堆栈溢出是否正确

我想还有下一个项目;之后马上打电话

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

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

它所做的是与事件循环协同工作,避免有一个真正长时间运行的作业占用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();
…在这种情况下,可能会出现堆栈溢出,但编写这种代码的方式非常奇怪

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

在最高级别,将军。但是不同的环境将以如此不同的方式实现细节,以至于您确实需要特定于环境的信息来了解发生了什么

它是如何工作的

所有血淋淋的细节都在说明书中,主要是在名为

有几个关键方面:

当HTML规范调用作业时,JavaScript规范的术语或任务中有一个作业队列等待JavaScript引擎运行。当引擎完成一个作业时,它将开始处理队列中的下一个作业/任务(如果有)

一旦作业开始,它就会一直运行,直到完成为止;没有其他工作可以打断它。这称为从运行到完成,对于定义JavaScript的工作方式非常重要

作业队列按顺序处理

请考虑这个代码:

console.log("one");
setTimeout(function() {
    console.log("two");
}, 1000);
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);
var list = readHugeList();
var nextListItem = function() {
    var item = list.pop();
    if (item) {
        // process the list item...
        setTimeout(nextListItem, 0);
    }
};
如果您通过NodeJS或浏览器运行该代码,忽略一些不相关的细节会发生以下情况:

开始时,队列为空,JavaScript引擎处于空闲状态 环境节点,浏览器将作业排队以运行脚本 JavaScript引擎将拾取作业并运行脚本: 它输出一个 它为我们提供给setTimeout的匿名函数设置了一个计时器 工作结束了 在某个时刻,环境中的计时器机制确定是时候调用回调了,因此它将作业排队以调用回调 JavaScript引擎从队列中获取该作业并运行该函数 它输出两个 工作结束了

现在考虑这个代码:

console.log("one");
setTimeout(function() {
    console.log("two");
}, 1000);
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);
var list = readHugeList();
var nextListItem = function() {
    var item = list.pop();
    if (item) {
        // process the list item...
        setTimeout(nextListItem, 0);
    }
};
如您所见,它将定时回调安排为500毫秒,然后再安排一次100毫秒。但是100毫秒回调中的代码至少需要1000毫秒才能运行。会发生什么

环境将作业排队以运行脚本 JS引擎将执行该任务 输出时间值和一,例如1470727293584一 在将来的500毫秒内,首先设置对函数的定时回调 在未来的100毫秒内设置对函数秒的定时回调 工作结束了 大约100毫秒后,环境将作业排队等待第二次运行 JavaScript引擎接收作业并运行函数 它开始正常工作了,所以真的只是忙着等待 大约400毫秒后,在设置计时器500毫秒后,环境将作业排队等待首先调用;由于JavaScript引擎忙于上一个作业,因此该作业位于队列中 与此同时,JavaScript引擎仍在处理第一个作业调用: 最终,JavaScript引擎从3开始所做的工作完成了 工作结束了 JavaScript引擎从队列中拾取下一个作业并运行对second的调用 它输出时间值和2,例如1470727294687 2 工作结束了 注意,当JavaScript繁忙时,环境做了一些事情;它把要做的工作排成队列

当JavaScript引擎繁忙时,环境可以做一些事情,这一点非常重要

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

我应该如何/何时/可以利用它

与其说是利用它,不如说是知道它在那里。同样重要的是要注意,虽然JavaScript已经运行到完成,但seman 但是,它运行的环境可能同时在做其他事情,即使JavaScript代码正在运行

ECMAScript-6/2015中引入的关于事件循环的任何更改

不完全是这样,尽管它在规范中的定义比以前更加完整。最接近更改的可能是与承诺有关:您使用then或catch计划的回调将始终作为作业排队,它永远不会同步运行。这是JavaScript规范第一次定义异步发生的事情,ajax和计时器不属于JavaScript规范的一部分

下面是您的提问:

考虑以下代码:

console.log("one");
setTimeout(function() {
    console.log("two");
}, 1000);
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);
var list = readHugeList();
var nextListItem = function() {
    var item = list.pop();
    if (item) {
        // process the list item...
        setTimeout(nextListItem, 0);
    }
};
这里假设setTimeout使用事件循环来防止堆栈溢出是否正确

我想还有下一个项目;之后马上打电话

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

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

它所做的是与事件循环协同工作,避免有一个真正长时间运行的作业占用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();


…在这种情况下,可能会出现堆栈溢出,但编写这种代码是一种非常奇怪的方式。

AFAIK ecmascript根本不定义事件循环。所以不存在JavaScript事件循环。它依赖于实现,例如nodejs。您想要什么级别的答案?它是如何工作的,从下面的示例到如何使用setTimeout,都可以理解为C@freakish:是的,从ES2015规范开始。它称之为作业队列。如果你想要一个简单的js示例,已经有很多重复的。没有理由不关闭它。下面是我写的3个答案,在不同的细节层次上,描述了事件循环:,AFAIK ecmascript根本没有定义事件循环。所以不存在JavaScript事件循环。它依赖于实现,例如nodejs。您想要什么级别的答案?它是如何工作的,从下面的示例到如何使用setTimeout,都可以理解为C@freakish:是的,从ES2015规范开始。它称之为作业队列。如果你想要一个简单的js示例,已经有很多重复的。没有理由不结束这个。下面是我写的3个答案,在不同的细节层次上,描述了事件循环:,这是一个非常详细的答案。非常感谢。如果我有一个递归函数来访问一个非常大的列表,那么递归函数中用于防止堆栈溢出的settimeout函数会被称为利用事件循环吗?@Stadium\u daddy:我不知道如何在真正的递归函数中使用settimeout,因为在作业结束之前堆栈必须展开,这意味着在计时器回调运行之前,函数已终止。但是,使用setTimeout来安排在当前作业完成后发生的事情是一种常见的技术,并且肯定会利用队列。假设您做了一些您知道会立即将事件回调排队的事情。您可以使用值为0的SETTIMEOUT来调度在事件回调之后运行的一些代码RO,知道队列是按顺序处理的。请考虑此代码:var列表= Read HuffGIST;var nextListItem=函数{var item=list.pop;如果项{//处理列表项…setTimeout nextListItem,0;};假设setTimeout在这里使用事件循环来防止堆栈溢出是正确的吗?@stadium\u daddy:不正确,除非代码一开始写得有点奇怪,但它在做其他重要的事情。我在答案的末尾加了一句,请原谅,先生。请看我更新的问题。这是一个非常详细的答案。非常感谢。如果我有一个递归函数来访问一个非常大的列表,那么递归函数中用于防止堆栈溢出的settimeout函数会被称为利用事件循环吗?@Stadium\u daddy:我不知道如何在真正的递归函数中使用settimeout,因为在作业结束之前堆栈必须展开,这意味着在计时器回调运行之前,函数已终止。但是,使用setTimeout来安排在当前作业完成后发生的事情是一种常见的技术,并且肯定会利用队列。假设您做了一些您知道会立即将事件回调排队的事情。您可以使用值为0的SETTIMEUT来安排在事件回调之后运行的一些代码RO,知道队列是按顺序处理的。请考虑此代码:var列表= Read Load
eList;var nextListItem=函数{var item=list.pop;如果项{//处理列表项…setTimeout nextListItem,0;};假设setTimeout在这里使用事件循环来防止堆栈溢出是正确的吗?@stadium\u daddy:不正确,除非代码一开始写得有点奇怪,但它在做其他重要的事情。我在答案的末尾加了一句,请原谅,先生。请看一下我的最新问题。