javascript中事件循环机制
2017-08-12 17:17
363 查看
JavaScript 的并发模型基于 “事件循环”。这个模型与像 C 或者 Java 这种其它语言中的模型着实不同。
可视化描述
栈
函数调用形成了一个栈帧。
当调用 bar 时,创建了第一个帧 ,帧中包含了 bar 的参数和局部变量。当 bar 调用 foo 时,第二个帧就被创建,并被压到第一个帧之上,帧中包含了 foo 的参数和局部变量。当 foo 返回时,最上层的帧就被弹出栈(剩下 bar 函数的调用帧 )。当 bar 返回的时候,栈就空了。
堆
对象被分配在一个堆中,一个用以表示一个内存中大的未被组织的区域。
队列
一个 JavaScript 运行时包含了一个待处理的消息队列。每一个消息都与一个函数相关联。当栈为空时,从队列中取出一个消息进行处理。这个处理过程包含了调用与这个消息相关联的函数(以及因而创建了一个初始堆栈帧)。当栈再次为空的时候,也就意味着消息处理结束。
while (queue.waitForMessage()) {
queue.processNextMessage();
}
如果当前没有任何消息,queue.waitForMessage 会同步等待消息到来。
“执行至完成”
每一个消息执行完成后,其它消息才会被执行。当你分析你的程序时,这点提供了一些优秀的特性,包括当一个函数运行时,它不能被取代且会在其它代码运行前先完成(而且能够修改这个函数控制的数据)。这点与C语言不同。例如,C语言中当一个程序在一个线程中运行时,它可以在任何点停止且可以在其它线程中运行其它代码。
这个模型的一个缺点在于当一个消息的完成耗时过长,网络应用无法处理用户的交互如点击或者滚动。浏览器用“程序需要过长时间运行”的对话框来缓解这个问题。一个比较好的解决方案是使消息处理变短且如果可能的话,将一个消息拆分成几个消息。
添加消息
在浏览器里,当一个事件出现且有一个事件监听器被绑定时,消息会被随时添加。如果没有事件监听器,事件会丢失。所以点击一个附带点击事件处理函数的元素会添加一个消息。其它事件亦然。
调用 setTimeout 函数会在一个时间段过去后在队列中添加一个消息。这个时间段作为函数的第二个参数被传入。如果队列中没有其它消息,消息会被马上处理。但是,如果有其它消息,setTimeout 消息必须等待其它消息处理完。因此第二个参数仅仅表
4000
示最少的时间 而非确切的时间。
零延迟
零延迟 (Zero delay) 并不是意味着回调会立即执行。在零延迟调用 setTimeout 时,其并不是过了给定的时间间隔后就马上执行回调函数。其等待的时间基于队列里正在等待的消息数量。在下面的例子中,”this is just a message” 将会在回调 (callback) 获得处理之前输出到控制台,这是因为延迟是要求运行时 (runtime) 处理请求所需的最小时间,但不是有所保证的时间。
多个运行时互相通信
一个 web worker 或者一个跨域的 iframe 都有它们自己的栈,堆和消息队列。两个不同的运行时只有通过 postMessage 方法进行通信。这个方法会给另一个运行时添加一个消息如果后者监听了 message 事件。
绝不阻塞Edit
一个很有趣的事件循环 (event loop) 模型特性在于,Javascript 跟许多其它语言不同,它永不阻塞。通常由事件或者回调函数进行 I/O (input/output)处理 。所以当一个应用正等待 IndexedDB 的查询的返回或者一个 XHR 的请求返回时,它仍然可以处理其它事情例如用户输入。
例外是存在的,如 alert 或者同步 XHR,但应该尽量避免使用它们。注意,例外的例外也是存在的(但通常是实现错误而非其它原因)。
运行时概念Edit
下面的内容解释了一个理论上的模型。现代 JavaScript 引擎着重实现和优化了描述的几个语义。可视化描述
栈
函数调用形成了一个栈帧。
function foo(b) { var a = 10; return a + b + 11; } function bar(x) { var y = 3; return foo(x * y); } console.log(bar(7));
当调用 bar 时,创建了第一个帧 ,帧中包含了 bar 的参数和局部变量。当 bar 调用 foo 时,第二个帧就被创建,并被压到第一个帧之上,帧中包含了 foo 的参数和局部变量。当 foo 返回时,最上层的帧就被弹出栈(剩下 bar 函数的调用帧 )。当 bar 返回的时候,栈就空了。
堆
对象被分配在一个堆中,一个用以表示一个内存中大的未被组织的区域。
队列
一个 JavaScript 运行时包含了一个待处理的消息队列。每一个消息都与一个函数相关联。当栈为空时,从队列中取出一个消息进行处理。这个处理过程包含了调用与这个消息相关联的函数(以及因而创建了一个初始堆栈帧)。当栈再次为空的时候,也就意味着消息处理结束。
事件循环Edit
之所以称为 事件循环,是因为它经常被用于类似如下的方式来实现:while (queue.waitForMessage()) {
queue.processNextMessage();
}
如果当前没有任何消息,queue.waitForMessage 会同步等待消息到来。
“执行至完成”
每一个消息执行完成后,其它消息才会被执行。当你分析你的程序时,这点提供了一些优秀的特性,包括当一个函数运行时,它不能被取代且会在其它代码运行前先完成(而且能够修改这个函数控制的数据)。这点与C语言不同。例如,C语言中当一个程序在一个线程中运行时,它可以在任何点停止且可以在其它线程中运行其它代码。
这个模型的一个缺点在于当一个消息的完成耗时过长,网络应用无法处理用户的交互如点击或者滚动。浏览器用“程序需要过长时间运行”的对话框来缓解这个问题。一个比较好的解决方案是使消息处理变短且如果可能的话,将一个消息拆分成几个消息。
添加消息
在浏览器里,当一个事件出现且有一个事件监听器被绑定时,消息会被随时添加。如果没有事件监听器,事件会丢失。所以点击一个附带点击事件处理函数的元素会添加一个消息。其它事件亦然。
调用 setTimeout 函数会在一个时间段过去后在队列中添加一个消息。这个时间段作为函数的第二个参数被传入。如果队列中没有其它消息,消息会被马上处理。但是,如果有其它消息,setTimeout 消息必须等待其它消息处理完。因此第二个参数仅仅表
4000
示最少的时间 而非确切的时间。
零延迟
零延迟 (Zero delay) 并不是意味着回调会立即执行。在零延迟调用 setTimeout 时,其并不是过了给定的时间间隔后就马上执行回调函数。其等待的时间基于队列里正在等待的消息数量。在下面的例子中,”this is just a message” 将会在回调 (callback) 获得处理之前输出到控制台,这是因为延迟是要求运行时 (runtime) 处理请求所需的最小时间,但不是有所保证的时间。
(function () { console.log('this is the start'); setTimeout(function cb() { console.log('this is a msg from call back'); }); console.log('this is just a message'); setTimeout(function cb1() { console.log('this is a msg from call back1'); }, 0); console.log('this is the end'); })(); // "this is the start" // "this is just a message" // "this is the end" // "this is a msg from call back" // "this is a msg from call back1"
多个运行时互相通信
一个 web worker 或者一个跨域的 iframe 都有它们自己的栈,堆和消息队列。两个不同的运行时只有通过 postMessage 方法进行通信。这个方法会给另一个运行时添加一个消息如果后者监听了 message 事件。
绝不阻塞Edit
一个很有趣的事件循环 (event loop) 模型特性在于,Javascript 跟许多其它语言不同,它永不阻塞。通常由事件或者回调函数进行 I/O (input/output)处理 。所以当一个应用正等待 IndexedDB 的查询的返回或者一个 XHR 的请求返回时,它仍然可以处理其它事情例如用户输入。
例外是存在的,如 alert 或者同步 XHR,但应该尽量避免使用它们。注意,例外的例外也是存在的(但通常是实现错误而非其它原因)。
相关文章推荐
- 【JavaScript 学习--12】JS深入理解调用栈,事件循环机制,回调队列
- javascript事件循环机制
- 详解JavaScript事件循环机制
- JavaScript运行机制之事件循环(Event Loop)详解
- JavaScript事件循环(Event Loop)机制
- 深入理解JavaScript事件循环机制
- JavaScript事件循环机制
- JavaScript运行机制之事件循环(Event Loop)详解
- 详解JavaScript中的Event Loop(事件循环)机制
- javascript循环变量注册dom事件 之强大的闭包
- Javascript事件注册机制--同时支持三种事件模型的javascript
- 深入理解JavaScript的闭包特性 如何给循环中的对象添加事件(转载)
- javascript中循环绑定监听事件的解决方案
- javascript的事件机制
- javascript的事件机制
- 【初窥javascript奥秘之事件机制】论“点透”与“鬼点击”
- Javascript事件注册机制--同时支持三种事件模型的javascript(转)
- 本周任务,对javascript事件机制进行思考 提供以下 几个博文 论点:
- Javascript中事件捕获、事件冒泡以及事件委托机制
- javascript事件捕获机制,dom tree