首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >了解事件循环

了解事件循环
EN

Stack Overflow用户
提问于 2014-02-06 15:54:41
回答 4查看 55.2K关注 0票数 151

我正在考虑这个问题,这就是我想出来的:

让我们看看下面的代码:

代码语言:javascript
复制
console.clear();
console.log("a");
setTimeout(function(){console.log("b");},1000);
console.log("c");
setTimeout(function(){console.log("d");},0);

一个请求传入,JS引擎开始一步一步地执行上述代码。前两个呼叫是同步调用。但是当涉及到setTimeout方法时,它就变成了异步执行。但是JS立即从它返回并继续执行,这称为Non-BlockingAsync。并继续致力于其他方面的工作。

此执行的结果如下:

C、d、b

因此,基本上第二个setTimeout首先完成,它的回调函数执行得比第一个要早,这是有意义的。

我们在这里讨论的是单线程应用程序。JS引擎一直在执行这个命令,除非它完成了第一个请求,否则它不会转到第二个请求。但好的是,它不会等待像setTimeout这样的阻塞操作来解决,所以它会更快,因为它接受新的传入请求。

但我的问题是围绕以下几点提出的:

#1:如果我们谈论的是一个单线程应用程序,那么在JS引擎接受更多请求并执行它们时,有什么机制会处理setTimeouts呢?单线程如何继续处理其他请求?在其他请求不断进入并被执行时,什么在setTimeout上有效。

#2:如果这些setTimeout函数在幕后执行,而更多的请求进入并正在执行,那么在幕后执行异步执行是什么呢?我们所说的EventLoop是什么?

#3:,但是不应该将整个方法放在EventLoop中,以便执行整个事情并调用回调方法吗?在谈到回调函数时,我就是这样理解的:

代码语言:javascript
复制
function downloadFile(filePath, callback)
{
   blah.downloadFile(filePath);
   callback();
}

但是在这种情况下,JS引擎如何知道它是否是一个异步函数,以便它可以将回调放在EventLoop中?也许类似于async关键字的C#,或者某种表示JS引擎将要使用的方法的属性,是一个异步方法,应该相应地处理。

#4:,但文章的说法与我对事情可能如何运作的猜测完全相反:

事件循环是回调函数的队列。当执行异步函数时,回调函数将被推入队列。直到执行了异步函数之后的代码,JavaScript引擎才开始处理事件循环。

#5:,这里有一幅图像,可能会很有帮助,但是图片中的第一个解释是说的和问题4中提到的完全一样:

那么,我在这里的问题是,得到一些澄清上面列出的项目?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-02-06 16:34:56

1:如果我们谈论的是单线程应用程序,那么在JS引擎接受更多请求并执行它们时,setTimeouts进程是什么进程?这个单线程不是会继续处理其他请求吗?然后,当其他请求不断出现并被执行时,谁将继续处理setTimeout。

节点进程中只有一个线程将实际执行程序的JavaScript。但是,在节点本身中,实际上有几个线程处理事件循环机制的操作,这包括一个IO线程池和少数其他线程。关键是这些线程的数量与处理并发连接的数量不相对应,就像在线程每个连接并发模型中那样。

现在关于“执行setTimeouts",当您调用setTimeout时,所有节点所做的基本上都是更新将来一次执行的函数的数据结构。它基本上有一堆需要做的事情队列和事件循环中的每一个“滴答”,它选择一个,从队列中删除它,并运行它。

需要理解的一个关键问题是,节点在大部分的繁重工作中都依赖于操作系统。因此,传入的网络请求实际上是由操作系统本身跟踪的,当节点准备处理一个请求时,它只是使用一个系统调用来向OS请求一个网络请求,并准备好要处理的数据。那么多的IO“工作”节点做的要么是“嘿OS,有网络连接的数据可以读取吗?”或者“嘿,OS,我的任何优秀的文件系统调用都准备好了吗?”节点将根据其内部算法和事件循环引擎的设计,选择JavaScript中的一个“滴答”执行,运行它,然后再次重复该过程。这就是事件循环的含义。节点基本上在任何时候都决定“我应该运行的下一个小JavaScript是什么?”,然后运行它。这是操作系统完成IO的因素,以及通过调用setTimeoutprocess.nextTicksetTimeout中排队的事情。

2:如果这些setTimeout将在更多的请求出现、传入和执行时在幕后执行,那么幕后执行异步执行的事情就是我们正在讨论的EventLoop?

没有JavaScript会在幕后执行。程序中的所有JavaScript都在前面和中心运行,一次运行一个。幕后发生的事情是,操作系统处理IO,节点等待准备就绪,节点管理等待执行的javascript队列。

3: JS引擎如何知道它是否是一个异步函数,以便将其放入EventLoop中?

节点核心中有一组固定的函数是异步的,因为它们进行系统调用,节点知道这些函数是什么,因为它们必须调用操作系统或C++。基本上,所有网络和文件系统IO以及子进程交互都是异步的,JavaScript使节点异步运行的唯一方法是调用节点核心库提供的异步函数之一。即使您使用的npm包定义了它自己的API,为了产生事件循环,最终npm包的代码将调用节点核心的异步函数之一,这时节点知道滴答已经完成,并且可以再次启动事件循环算法。

4事件循环是回调函数的队列。当执行异步函数时,回调函数将被推入队列。直到执行了异步函数之后的代码,JavaScript引擎才开始处理事件循环。

是的,这是真的,但这是误导。关键是正常的模式是:

代码语言:javascript
复制
//Let's say this code is running in tick 1
fs.readFile("/home/barney/colors.txt", function (error, data) {
  //The code inside this callback function will absolutely NOT run in tick 1
  //It will run in some tick >= 2
});
//This code will absolutely also run in tick 1
//HOWEVER, typically there's not much else to do here,
//so at some point soon after queueing up some async IO, this tick
//will have nothing useful to do so it will just end because the IO result
//is necessary before anything useful can be done

所以是的,你可以完全阻止事件循环,只需在内存中同步计数斐波纳契数,所有的时间都是一样的,是的,这会完全冻结你的程序。这是合作并发。JavaScript的每一个滴答都必须在合理的时间内产生事件循环,否则整个体系结构就会失败。

票数 97
EN

Stack Overflow用户

发布于 2014-02-06 16:33:33

不要认为主机进程是单线程的,它们不是。单线程是执行javascript代码的主机进程的一部分。

除了背景工作者,但这会使情况变得复杂.

因此,所有js代码都在同一个线程中运行,不可能同时运行js代码的两个不同部分(因此,您不需要管理并发性梦魇)。

正在执行的js代码是宿主进程从事件循环中获取的最后一段代码。在您的代码中,您基本上可以做两件事:运行同步指令,以及在将来发生某些事件时调度要执行的函数。

这是我的心理表示法(注意:只是我不知道浏览器实现的细节!)示例代码的示例代码:

代码语言:javascript
复制
console.clear();                                   //exec sync
console.log("a");                                  //exec sync
setTimeout(                //schedule inAWhile to be executed at now +1 s 
    function inAWhile(){
        console.log("b");
    },1000);    
console.log("c");                                  //exec sync
setTimeout(
    function justNow(){          //schedule justNow to be executed just now
        console.log("d");
},0);       

当您的代码正在运行时,主机进程中的另一个线程将跟踪正在发生的所有系统事件(单击UI、读取文件、接收网络数据包等)。

当您的代码完成时,它将从事件循环中删除,主机进程将返回到检查它,以查看是否有更多的代码要运行。事件循环包含两个事件处理程序:一个现在执行( justNow函数),另一个在第二个( inAWhile函数)内执行。

主机进程现在尝试匹配发生的所有事件,以查看是否有为它们注册的处理程序。它发现justNow正在等待的事件已经发生,所以它开始运行它的代码。当justNow函数退出时,它将再次检查事件循环,查找事件上的处理程序。假设1已经通过,它运行inAWhile函数,等等.

票数 12
EN

Stack Overflow用户

发布于 2021-05-13 20:11:53

事件循环有一个简单的作业 - 来监视调用堆栈回调队列微任务队列。如果调用堆栈为空,则事件循环将从微任务队列接收第一个事件,然后从回调队列接收第一个事件,并将其推送到有效运行它的调用堆栈中。这样的迭代被称为事件循环中的滴答。

大多数开发人员都知道,Javascript是单线程的,这意味着javascript中的两个语句不能并行执行,这是正确的。执行会逐行进行,这意味着每个javascript语句都是同步和阻塞的。但是有一种方法可以异步运行您的代码,如果您使用setTimeout()函数,这是一个由浏览器提供的Web,它确保您的代码在指定的时间(毫秒)后执行。

示例:

代码语言:javascript
复制
console.log("Start");

setTimeout(function cbT(){
console.log("Set time out");
},5000);

fetch("http://developerstips.com/").then(function cbF(){
console.log("Call back from developerstips");
});

// Millions of line code
// for example it will take 10000 millisecond to execute

console.log("End");

setTimeout将回调函数作为第一个参数,以毫秒为单位的时间作为第二个参数。在浏览器控制台中执行上述语句后,它将打印

代码语言:javascript
复制
Start
End
Call back from developerstips
Set time out

注意事项:异步代码在执行所有同步代码之后运行。

理解代码如何逐行执行

JS引擎执行第一行,并在控制台中打印"Start“。

在第2行中,它看到名为setTimeout的cbT函数,JS引擎将cbT函数推入callBack队列。

在此之后,指针将直接跳转到第7号行,在那里它将看到queue和JS引擎将cbF函数推送到微任务队列中。

然后它将执行数百万行代码并结束它将打印" end“。

执行主线程结束后,事件循环首先检查微任务队列,然后调用回队列。在本例中,它从微任务队列中提取cbF函数并将其推入调用堆栈,然后从回调队列中选择cbT函数并推入调用堆栈。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21607692

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档