ECMAScript规范没有提到事件循环。相反,事件循环在HTML规范中有详细说明,尽管事件循环是在HTML规范中定义的,可是在其余环境中,好比Nodejs,也使用了它。javascript
为了协调事件,用户交互,脚本,渲染,网络等,用户代理必须使用本节所述的event loop。 Events,Parsing,Callbacks,Using a resource,Reacting to DOM manipulation
事件,用户交互,脚本,渲染,网络这些都是咱们所熟悉的东西,他们都是由event loop协调的。触发一个click事件,进行一次ajax请求,背后都有event loop在运做。html
An event loop has one or more task queues. A task queue is a set of tasks.vue
一个 event loop有一个或多个任务队列,一个任务队列是一个多任务的集合。java
Task queues are sets, not queues, because step one of the event loop processing model grabs the first runnable task from the chosen queue, instead of dequeuing the first task.node
任务队列是集合,而不是队列,由于事件循环处理模型的第一步从选择的队列中获取第一个可运行的任务,而不是退出第一个任务。web
The microtask queue is not a task queue.ajax
微任务队列不是任务队列。segmentfault
在规范的Processing model定义了event loop的循环过程:api
1.在tasks队列中选择最老的一个task,用户代理能够选择任何task队列,
若是没有可选的任务,则跳到下边的microtasks步骤。
2.设置oldestTask成为任务队列中第一个可运行的任务,并将其从任务队列中删除。
5.运行oldestTask里面的一系列脚本
7.从其任务队列中删除oldestTask
8.Microtasks: 执行microtasks任务检查点。(也就是执行microtasks队列里的任务)
11.更新渲染(Update the rendering)...
复制代码
所作的就是执行microtask队列里的任务。何时会调用microtask checkpoint呢?promise
<div class="outer">
<div class="inner"></div>
</div>
<script>
// Let's get hold of those elements var outer = document.querySelector('.outer'); var inner = document.querySelector('.inner'); // Let's listen for attribute changes on the
// outer element
new MutationObserver(function() {
console.log('mutate');
}).observe(outer, {
attributes: true
});
// Here's a click listener… function onClick() { console.log('click'); setTimeout(function() { console.log('timeout'); }, 0); Promise.resolve().then(function() { console.log('promise'); }); outer.setAttribute('data-random', Math.random()); } // …which we'll attach to both elements
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
</script>
复制代码
点击打印结果
click
promise
mutate
click
promise
mutate
timeout
timeout
复制代码
此代码出处和解释> jakearchibald.com/2015/tasks-…
事件循环容许Node.js执行非阻塞的I/O操做(尽管JavaScript是单线程的),方法是尽量地将操做卸载到系统内核。
下图简要的概述了event loop的操做顺序:
注:每个框表明event loop中的一个阶段
每一个阶段都有一个FIFO(先进先出)的回调队列等待执行。虽然每一个阶段都有其独特之处,但整体而言,当event loop进入到指定阶段后,它会执行该阶段的任何操做,并执行对应的回调直到队列中没有可执行回调或者达到回调执行上限,然后event loop会进入下一阶段。
因为任何这些阶段的操做可能产生更多操做,内核也会将新的事件推入到poll阶段的队列中,因此新的poll事件被容许在处理poll事件时继续加入队,这也意味着长时间运行的回调能够容许poll阶段运行的时间比计时器的阈值要长
Between each run of the event loop, Node.js checks if it is waiting for any asynchronous I/O or timers and shuts down cleanly if there are not any.
在每次运行事件循环之间,Node.js检查它是否在等待任何异步I/O或计时器,若是没有,则干净地关闭。
两者的调用顺序取决于它们的执行上下文。若是二者都在主模块被调用,那么其回调被执行的时间点就取决于处理过程的性能(这可能被运行在同一台机器上的其余应用影响)
好比说,若是下列脚本不是在I/O循环中运行,这两种定时器运行的顺序是不必定的
// timeout_vs_immediate.js
setTimeout(function timeout() {
console.log('timeout');
}, 0);
setImmediate(function immediate() {
console.log('immediate');
});
复制代码
$ node timeout_vs_immediate.js
timeout
immediate
$ node timeout_vs_immediate.js
immediate
timeout
复制代码
可是若是你把上面的代码置于I/O循环中,setImmediate回调会被优先执行:
const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
复制代码
$ node timeout_vs_immediate.js
immediate
timeout
$ node timeout_vs_immediate.js
immediate
timeout
复制代码
使用setImmediate()而不是setTimeout()的主要好处是:若是代码是在I/O循环中调用,那么setImmediate()老是优先于其余定时器(不管有多少定时器存在)
When delay is larger than 2147483647 or less than 1, the delay will be set to 1.
setTimeout(()=> {
console.log(1);
},1);
setTimeout(()=> {
console.log(0);
},0);
# output 能够看到1在前 0在后
1 2
0 4
复制代码
在浏览器中,setTimeout()/setInterval() 的每调用一次定时器的最小间隔是4ms,这一般是因为函数嵌套致使(嵌套层级达到必定深度),或者是因为已经执行的setInterval的回调函数阻塞致使的。例如:
let i = 0
let now = Date.now()
function f(){
let newD = Date.now()
console.log(newD-now)
now = newD
}
function cb() {
f();
i++
if(i>10) return
setTimeout(cb, 0);
}
setTimeout(cb, 0)
复制代码
你可能已经注意到process.nextTick()不在上面的图表中,即便它也是异步api。这是由于严格意义上来讲process.nextTick()不属于event loop中的一部分,它会忽略event loop当前正在执行的阶段,而直接处理nextTickQueue中的内容。
回过头看一下图表,你在任何给定阶段调用process.nextTick(),在继续event loop以前,全部传入process.nextTick()的回调都会被执行。这可能会致使一些很差的状况,由于它容许你递归调用process.nextTick()从而使得event loop没法进入poll阶段,致使没法接收到新的 I/O事件
这里有两个主要的缘由
如下截图来源:《深刻浅出node.js》