【译】并发模型和事件循环

JavaScript有一个基于“事件循环”的并发模型。这种模型完全不同于从其他语言的,如C和java。

运行时的概念

下面的章节解释一个理论模型。现代JavaScript引擎实现和优化所描述的语义。

直观表示

函数调用形成一个堆栈的帧。

当调用bar函数时,第一个帧被创建,它包含了bar的参数和局部变量。当bar调用foo时,第二个帧被创建并被推到第一个帧上,该第一个帧包含foo的参数和局部变量。返回时,顶部帧元素从堆栈中弹出(只留下bar的调用帧)。当bar返回时,堆栈为空。 

 1 function foo(b) {
 2   var a = 10;
 3   return a + b + 11;
 4 }
 5
 6 function bar(x) {
 7   var y = 3;
 8   return foo(x * y);
 9 }
10
11 console.log(bar(7));

对象在堆中分配,该堆只是一个名称,用来表示内存的一个很大的非结构化区域。

队列

JavaScript运行时包含消息队列,它是要处理的消息的列表。消息队列是一个与每个消息关联的函数。当堆栈有足够的容量时,将从队列中取出消息并进行处理。处理包括调用关联函数(从而创建初始堆栈帧)。当堆栈再次变空时,消息处理结束。

事件循环

事件循环这个名字的由来,因为它通常是如何实现的,它通常类似于:

while (queue.waitForMessage()) {
  queue.processNextMessage();
} 

当queue.waitForMessage被处理完毕时,才处理queue.processNextMessage。

完全处理

在处理任何其他消息之前,每个消息都被完全处理。这特别有助于你推理程序。因为每当一个函数运行,它不能捷足先登,要将之前的任何其他代码完全运行(可以修改数据的函数处理)。这与C不同,例如,如果一个函数在一个线程中运行,它可以在任何点停止运行另一个线程中的其他代码。
此模型的一个缺点是,如果消息需要太长时间完成,Web应用程序无法处理用户交互,如单击或滚动。浏览器用“脚本运行太长”对话框来缓解这个问题。一个好的做法是使消息处理短,如果可能的话,削减一个消息到几个消息。

添加消息

在web浏览器中,任何时候一个事件发生时都会添加消息,并附加有事件侦听器。如果没有侦听器,则事件丢失。因此,单击带有单击事件处理程序的元素将添加一条消息,与其他事件相同。 
调用setTimeout将在指定的时间后(你设置的第二个参数)向队列中添加消息。如果在队列中没有其他消息,这消息会被立刻处理;不过,如果有消息,setTimeout消息将不得不等待其他要处理的消息先处理完毕。因此,第二个参数表示最小时间,而不是保证时间。

零延迟

零延迟实际上并不意味着在零毫秒后执行回调函数。调用setTimeout(零),不一定会在给定的时间间隔后执行回调函数。执行取决于队列中等待任务的数目。在下面的例子中的“this is just a message”将被先于它前面的setTimeout函数(0延迟)处理。因为延迟是运行时处理请求所需的最小时间,但不是保证时间。

 1 (function() {
 2     console.log(‘this is the start‘);
 3
 4     setTimeout(function cb() {
 5         console.log(‘this is a msg from call back‘);
 6     });
 7
 8     console.log(‘this is just a message‘);
 9
10     setTimeout(function cb1() {
11         console.log(‘this is a msg from call back1‘);
12     }, 0);
13     console.log(‘this is the end‘);
14 })();
15 // "this is the start"
16 // "this is just a message"
17 // "this is the end"
18 // "this is a msg from call back"
19 // "this is a msg from call back1"

几个运行时之间的通信

一个web任务执行器或跨域iframe都拥有自己的堆栈,堆,和消息队列。两个不同的运行时只能通过postMessage方法发送消息进行通信。如果后者监听消息事件,则该方法向其他运行时添加消息。

从不堵塞

事件循环模型的一个非常有趣的特性是JavaScript从不阻塞,不像许多其他语言。处理I/O通常是通过事件和回调函数进行的,因此当应用程序等待一个IndexedDB查询返回或XHR请求返回时,它还可以处理其他事情,比如用户输入。

有遗留的例外存在,像警报或同步XHR,但它被认为是一个很好的实践来避免它们。请注意,异常的异常确实存在(但通常是实现错误,而不是其他)。

原文链接,翻译难免有出入,欢迎交流和斧正。

时间: 2024-10-21 07:45:20

【译】并发模型和事件循环的相关文章

QT中的线程与事件循环理解(2)

1. Qt多线程与Qobject的关系 每一个 Qt 应用程序至少有一个事件循环,就是调用了QCoreApplication::exec()的那个事件循环.不过,QThread也可以开启事件循环.只不过这是一个受限于线程内部的事件循环.因此我们将处于调用main()函数的那个线程,并且由QCoreApplication::exec()创建开启的那个事件循环成为主事件循环,或者直接叫主循环.注意,QCoreApplication::exec()只能在调用main()函数的线程调用.主循环所在的线程

初学Node(四)事件循环

Node中的事件循环 事件循环是Node的核心,正是因为有了事件循环JS才能够在服务端占有一席之地.JS是一种单线程语言,但是它的执行环境是多线程的在加上JS的事件驱动这一特点,使使JS在执行的过程中没执行到一个异步操作就交由后台处理然后继续向下执行,在遇上一个异步操作又交由后台处理,JS的执行线程不会发生阻塞,一旦JS代码执行完毕就会去后台查看有没有满足条件的异步操作一旦有满足条件的就执行事先定义好的处理函数. 在Node中通过EventEmitter(事件发生器)来实现这种功能,EventE

Qt 的线程与事件循环——可打印threadid进行观察槽函数到底是在哪个线程里执行,学习moveToThread的使用)

周末天冷,索性把电脑抱到床上上网,这几天看了 dbzhang800 博客关于 Qt 事件循环的几篇 Blog,发现自己对 Qt 的事件循环有不少误解.从来只看到现象,这次借 dbzhang800 的博客,就代码论事,因此了解到一些 Qt 深层的实现,虽然是在 Qt 庞大的构架里只算的是冰山的一角,确让人颇为收益. 从 dbzhang800 的博客中转载两篇关于事件循环的文章,放在一起,写作备忘. 再次提到的一点是:事件循环和线程没有必然关系. QThread 的 run() 方法始终是在一个单独

QDialog 模态对话框与事件循环

起源 qtcn中文论坛中有网友问到: 假设程序正常运行时,只有一个简单的窗体A,此时只有一个GUI主线程,在这个主线程中有一个事件循环处理窗体上的事件.当此程序运行到某阶段时,弹出一个模态窗体B(书上说模态窗体是有其自己的事件循环的),此时模态窗体B是否会有一个对应的子线程处理其事件循环? 这儿其实有两个问题: 模态对话框 和 事件循环 没有必然联系 事件循环 和 子线程 没有必然联系 题外: 如果进一步呢?其实我们还可以说: 模态对话框 和 QDialog 没必要联系 QDialog 对话框

redis源码分析(2)——事件循环

redis作为服务器程序,网络IO处理是关键.redis不像memcached使用libevent,它实现了自己的IO事件框架,并且很简单.小巧.可以选择select.epoll.kqueue等实现. 作为 IO事件框架,需要抽象多种IO模型的共性,将整个过程主要抽象为: 1)初始化 2)添加.删除事件 3)等待事件发生 下面也按照这个步骤分析代码. (1)初始化 回忆一下redis的初始化过程中,initServer函数会调用aeCreateEventLoop创建event loop对象,对事

libev 默认事件循环初始化的解析

libev第一次进入的是默认的事件循环,这里将源码中执行的默认循环流程解析一下,要进入事件循环,如下例子 int main (void) { // use the default event loop unless you have special needs struct ev_loop *loop = EV_DEFAULT; // initialise an io watcher, then start it // this one will watch for stdin to becom

JavaScript:彻底理解同步、异步和事件循环(Event Loop) (转)

原文出处:https://segmentfault.com/a/1190000004322358 一. 单线程 我们常说"JavaScript是单线程的". 所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个.不妨叫它主线程. 但是实际上还存在其他的线程.例如:处理AJAX请求的线程.处理DOM事件的线程.定时器线程.读写文件的线程(例如在Node.js中)等等.这些线程可能存在于JS引擎之内,也可能存在于JS引擎之外,在此我们不做区分.不妨叫它们工作线程

JavaScript:同步、异步和事件循环

一. 单线程 我们常说“JavaScript是单线程的”. 所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个.不妨叫它主线程. 但是实际上还存在其他的线程.例如:处理AJAX请求的线程.处理DOM事件的线程.定时器线程.读写文件的线程(例如在Node.js中)等等.这些线程可能存在于JS引擎之内,也可能存在于JS引擎之外,在此我们不做区分.不妨叫它们工作线程. 二. 同步和异步 假设存在一个函数A: A(args...); 同步:如果在函数A返回的时候,调用者就能

关于Qt的事件循环以及QEventLoop的简单使用

1.一般我们的事件循环都是由exec()来开启的,例如下面的例子: 1 QCoreApplicaton::exec() 2 QApplication::exec() 3 QDialog::exec() 4 QThread::exec() 5 QDrag::exec() 6 QMenu::exec() 这些都开启了事件循环,事件循环首先是一个无限“循环”,程序在exec()里面无限循环,能让跟在exec()后面的代码得不到运行机会,直至程序从exec()跳出.从exec()跳出时,事件循环即被终止