共计 2397 个字符,预计需要花费 6 分钟才能阅读完成。
事件循环机制
在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的工作解决是比较复杂的,但关键步骤如下:
- 执行一个宏工作(栈中没有就从事件队列中获取)
- 执行过程中如果遇到微工作,就将它增加到微工作的工作队列中
- 宏工作执行结束后,立刻执行以后微工作队列中的所有微工作(顺次执行)
- 以后宏工作执行结束,开始查看渲染,而后 GUI 线程接管渲染
- 渲染结束后,JS 线程持续接管,开始下一个宏工作(从事件队列中获取)
流程图如下:
<img src=”https://noxussj.top:3000/41/1.png”></img>
那么什么是宏工作和微工作呢?
宏工作
(macro)task(又称之为宏工作),能够了解是每次执行栈执行的代码就是一个宏工作(包含每次从事件队列中获取一个事件回调并放到执行栈中执行)
浏览器为了可能使得 JS 外部 (macro)task 与 DOM 工作可能有序的执行, 会在一个(macro)task 执行完结后,在下一个(macro)task 执行开始前,对页面进行从新渲染
(macro)task 次要蕴含:script(整体代码)、setTimeout、setInterval
微工作
microtask(又称为微工作),能够了解是在以后(macro) task 执行完结后立刻执行的工作。也就是说,在以后(macro)task 工作后,下一个(macro)task 之前,在渲染之前。
所以它的响应速度相比 setTimeout(setTimeout 是(macro)task)会更快,因为无需等渲染。也就是说,在某一个 macrotask 执行完后,就会将在它执行期间产生的所有 microtask 都执行结束(在渲染前)
microtask 次要蕴含:Promise.then、await 办法前面的代码属于.then(await 相当于一个 Promise)
栗子
async function async1() {console.log('A');
await async2();
console.log('B');
}
async function async2() {console.log('C');
}
console.log('D');
setTimeout(function() {console.log('E');
});
async1();
new Promise(function(resolve) {console.log('F');
resolve();}).then(function() {console.log('G');
});
console.log('H');
首先咱们须要明确以下几件事件
工作队列次要包含以下 3 个,宏工作队列、微工作队列、执行栈
- 一开始执行栈,以及微工作队列为空,宏工作只有一个 script 代码块
- 执行栈为空时,就把下一个宏工作增加到执行栈中运行
- 开始运行宏工作 script
- 程序往下执行遇到了 console.log(‘D’),这个时候间接打印
后果为:// D - 而后持续往下执行遇到了 setTimeout,它属于宏工作所以先把它增加到宏工作队列中
工作队列状态如下
执行栈:script
宏工作队列:setTimeout
微工作队列:空 - 持续往下执行遇到了 async1()办法,运行该办法遇到了 console.log(‘A’),间接打印
后果为:// D A - 持续往下执行遇到了 async2()办法,运行该办法遇到了 console.log(‘C’),间接打印
后果为:// D A C - async2()办法内的程序都执行结束,回到上一层 async1()中,遇到 console.log(‘B’),它在 await async2() 的前面,所以属于异步并且增加到微工作队列中,而后回到最里面一层
工作队列状态如下
执行栈:script
宏工作队列:setTimeout
微工作队列:console.log(‘B’) - 持续往下执行遇到了 new Promise(),该作用域内同步工作。执行作用域内办法,遇到了 console.log(‘F’),间接打印
后果为:// D A C F - 持续往下执行遇到了.then 属于异步,将 then 外部的代码增加到微工作队列中
工作队列状态如下
执行栈:script
宏工作队列:setTimeout
微工作队列:console.log(‘B’)、console.log(‘G’) - 该 new Promise 办法执行结束,回到最初里面,遇到了 console.log(‘H’),间接打印
后果为:// D A C F H - 以后 script 代码块程序执行结束,也就是以后宏工作执行结束。在执行该宏工作的过程中,如果某个微工作曾经准备就绪好了会标记一个准备就绪的状态
- 将已就绪的微工作从微工作队列中增加到执行栈中
工作队列状态如下
执行栈:console.log(‘B’)、console.log(‘G’)
宏工作队列:setTimeout
微工作队列:空 - 开始运行执行栈的工作,按程序执行间接打印
后果为:// D A C F H B G
工作队列状态如下
执行栈:空
宏工作队列:setTimeout
微工作队列:空 - 以后的执行栈为空,则把宏工作队列中的 setTimeout 增加到执行栈中运行
- setTimeout 中遇到了 console.log(‘E’)间接打印
后果为:// D A C F H B G E - 以后执行栈已执行结束,检测是否有微工作(没有),检测是否有宏工作(没有)。整个程序执行结束
此题留下了一个问题,假如遇到多个 setTimeout 提早执行的工夫不同时,该如何执行?
参考资料
从一道题浅说 JavaScript 的事件循环
文章的内容 / 灵感都从下方内容中借鉴
- 【继续保护 / 更新 500+ 前端面试题 / 笔记】https://github.com/noxussj/In…
- 【大数据可视化图表插件】https://www.npmjs.com/package…
- 【利用 THREE.JS 实现 3D 城市建模(珠海市)】https://3d.noxussj.top/