Javascript 工作原理:Event loop
转载:原文链接
什么是事件循环(Event loop)?
我们从一个奇怪的说法开始-尽管允许异步JavaScript代码(如setTimeout我),但是直到ES6为止,JavaScript本身实际上从未内置任何直接的异步概念。除了在任何给定的时刻执行程序的单个块之外,JavaScript引擎从未做过其他任何事情。
那么,谁告诉JS引擎执行您的程序块呢?实际上,JS引擎并不是孤立运行的,而是在托管环境中运行的,对于大多数开发人员而言,它是典型的Web浏览器或Node.js。实际上,如今,JavaScript已嵌入从机器人到灯泡的各种设备中。每个单独的设备代表JS Engine的不同类型的托管环境。
所有环境中的共同点是称为事件循环的内置机制,该机制每次调用JS Engine时都会处理程序中多个块的执行
这意味着JS引擎只是任何任意JS代码的按需执行环境。安排事件(JS代码执行)的时间是周围的环境。
因此,例如,当您的JavaScript程序发出Ajax请求以从服务器获取某些数据时,您在函数中设置了“response”代码(“callback”),然后JS引擎告诉托管环境:
“嘿,我现在暂时暂停执行,但是只要您完成该网络请求,并且有一些数据,请回叫此函数。”
然后,将浏览器设置为侦听来自网络的响应,并在有需要返回给您的内容时,将通过将其插入事件循环中来安排要执行的回调函数。
让我们看下图:
这些Web API是什么?本质上,它们是您无法访问的线程,您可以对其进行调用。它们是启动并发性的浏览器的组成部分。如果您是Node.js开发人员,则这些都是C ++ API。
那么事件循环到底是什么?
事件循环有一个简单的工作-监听Call Stack 和Callback Queue。如果Call Stack 为空,则Event Loop将从queue中获取第一个事件,并将其推入Call Stack,从而有效地运行该事件。
这样的迭代在事件循环中称为tick每个事件只是一个函数回调。
console.log('Hi');
setTimeout(function cb1() {
console.log('cb1');
}, 5000);
console.log('Bye');
让我们“执行”这段代码,看看会发生什么:
- 状态很清楚。浏览器控制台已清除,并且调用堆栈为空。
- console.log('Hi')被添加到调用堆栈中
- console.log('Hi')被执行。
- console.log('Hi')从Call Stack.中删除。
- setTimeout(function cb1() { ... })被添加到Call Stack.中。
- setTimeout(function cb1() { ... })执行。浏览器将创建计时器作Web API的一部分。它会为您处理倒计时。
- setTimeout(function cb1() { ... })本身已经完成,并已从Call Stack.中删除。
- console.log('Bye')被添加到Call Stack.中。
- console.log('Bye')执行。
- console.log('Bye')从Call Stack中删除。
- 至少5000ms后,计时器完成,并将cb1回调推送到“Callback Queue”。
- Event loop cb1 从 Callback Queue 获取并将其推送到Call Stack.。
- cb1执行并添加console.log('cb1')到Call Stack.。
- console.log('cb1')执行。
- console.log('cb1')从Call Stack.中删除。
- cb1从Call Stack中删除。
快速回顾:
有趣的是,ES6指定了事件循环的工作方式,这意味着从技术上讲,它属于JS引擎职责范围之内,而JS引擎不再仅充当宿主环境角色。进行此更改的一个主要原因是在ES6中引入了Promises,因为后者需要访问对事件循环队列上的调度操作的直接,细粒度的控制(我们将在后面详细讨论)
setTimeout(…)如何工作的
重要的是要注意,setTimeout(…)它不会自动将callback放在 event loop queue中。它设置了一个计时器。当计时器到期时,环境会将您的callback放入event loop 中,以便将来的某个tick将其选中并执行。看一下这段代码:
setTimeout(myCallback, 1000);
这并不意味着myCallback将在1,000毫秒内执行,而是在1,000毫秒内myCallback将其添加到 event loop queue中。但是,queue中可能还包含其他较早添加的事件-您的回调将不得不等待。
看下面的代码:
setTimeout(function() {
console.log('callback');
}, 0);
console.log('Bye');
尽管等待时间设置为0ms,但在浏览器控制台中的结果将是以下内容:
Hi
Bye
callback