零延迟(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');
})();
运行时概念 Runtime
可视化描述
栈
函数调用形成了一个frames的栈。
调用g的时候,创建了第一个frame,包含了g的参数和局部变量。当 g 调用 f 的时候,第二个frame就被创建、并置于第一个frame之上,包含了f的参数和局部变量。当f返回时,最上层的frame就出栈了(剩下g函数调用的frame)。当g返回的时候,栈就空了。
堆
对象被分配在一个堆中,一个用以表示一个内存中大的未被组织的区域。
队列
一个JavaScript运行时包含了一个待处理的消息队列。每一个消息都与一个函数相关联。当栈为空时,从队列中取出一个消息进行处理。这个处理过程包含了调用与这个消息相关联的函数(以及因此而创建的一个初始栈结构)。当栈再次为空的时候,也就意味着消息处理结束。
单线程
Javascript是单线程的,在JS引擎中负责解释和执行JavaScript代码的线程只有一个(暂称为主线程)。
浏览器不是单线程的
虽然JS运行在浏览器中,是单线程的,每个window一个JS线程,但浏览器不是单线程的其可能有如下线程:
另外Event loop事件队列有单独的线程去处理(暂称为事件队列工作线程)。
同步和异步
如果在函数执行完成的时候,调用者就能够得到预期结果(即拿到了预期的返回值或者看到了预期的效果),那么这个函数就是同步的。 如果在函数执行完成的时候,调用者还不能够得到预期结果,而是需要在将来通过一定的手段得到,那么这个函数就是异步的。 总结一下,一个异步过程通常是这样的:
事件循环 Event Loop
类似如下的方式来实现:
如果当前没有任何消息,queue.waitForMessage 会同步等待消息到来。
"执行至完成"
每一个消息执行完成后,其它消息才会被执行。当一个函数运行时,它不能被取代且会在后续代码运行前先完成(而且能够修改这个函数控制的数据)。这点与C语言不同。例如,C语言中当一个程序在一个线程中运行时,它可以在任何点停止且可以在其它线程中运行其它代码。
这个模型的一个缺点在于当一个消息的完成耗时过长,网络应用无法处理用户的交互如点击或者滚动。浏览器用“程序需要过长时间运行”的对话框来缓解这个问题。一个比较好的解决方案是使消息处理变短。如果可能的话,将一个消息拆分成几个消息。
添加消息
在浏览器里,当一个事件出现且有一个事件监听器被绑定时,消息会被随时添加。所以点击一个附带点击事件处理函数的元素会添加一个消息。其它事件亦然。
调用 setTimeout 函数会在一个时间段过去后在队列中添加一个消息。这个时间段作为函数的第二个参数被传入。如果队列中没有其它消息,消息会被马上处理。但是,如果有其它消息,setTimeout消息必须等待其它消息处理完。因此第二个参数仅仅表示最少的时间 而非确切的时间。
零延迟
零延迟(Zero delay) 并不是意味着回调会立即执行。在零延迟调用setTimeout时,其并不是过了给定的时间间隔后就马上执行回调函数。其等待的时间基于队列里正在等待的消息数量。在下面的例子中,"this is just a message"在将会在回调(callback)获得处理之前输出到控制台,这是因为延迟是要求运行时(runtime)处理请求所需的最小时间,但不是确定的时间。
面试题:执行下面这段代码,执行后,在 5s 内点击两下,过一段时间(>5s)后,再点击两下,整个过程的输出结果?
几个运行时(Runtime)互相通信
一个web worker或者一个跨域的iframe都有它们自己的栈,堆和消息队列。两个不同的运行时只有通过postMessage方法进行通信。这个方法会给另一个运行时添加一个消息如果后者监听了message事件。
绝不阻塞
一个很有趣的事件循环(event loop)模型特性在于,Javascript跟其它语言不同,它永不阻塞。处理I/O (input/output)通常由事件或者回调函数进行实现。所以当一个应用正等待IndexedDB的查询的返回或者一个XHR(译者注:用于Ajax)的请求返回时,它仍然可以处理其它事情例如用户输入。
例外是存在的,如alert或者同步XHR,但避免以它们为最佳实践。
相关链接
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop https://segmentfault.com/a/1190000004322358 http://latentflip.com