浅聊事件循环:宏任务和微任务

微信扫一扫,分享到朋友圈

浅聊事件循环:宏任务和微任务

咱们先从js引擎开始。js是单线程的,也就是说所有的代码是一行接着一行运行的。此外js提供了宏任务和微任务这两个概念。先说宏任务。宏任务是一个外部脚本文件,一个用户交互触发的事件或一个setTimeout调用的回调函数。为了实现单线程这个概念,js有一个宏任务队列(先进先出),宏任务不断地创建出来塞到队尾,js引擎不断地从队首取任务出来执行。

分割大任务

现我们来模拟一个耗时很长的任务。

let i = 0;
let start = Date.now();
function count() {
//耗时长的任务
for (let j = 0; j < 1e9; j++) {
i++;
}
console.log("Done in " + (Date.now() - start) + 'ms');
}
count();
复制代码

在这个任务执行的过程,用户对页面的任何操作都将塞到这个任务的后面,因此造成一段时间没有响应,用户体验非常不好。 如果我们理解了以上的概念。我们就可以对代码进行改进。我们就大任务拆分成小任务。

let i = 0;
let start = Date.now();
function count() {
do {
i++;
} while (i % 1e6 != 0);
if (i == 1e9) {
console.log("Done in " + (Date.now() - start) + 'ms');
} else {
setTimeout(count);//注意!setTimeout会创建一个宏任务,塞到队尾。
}
}
count();
复制代码

之前的体验是

| 任务 |用户操作

现在是

| 任务 |用户操作| 任务 |用户操作| 任务 |

因为在处理小任务这个宏任务时,用户的操作事件也被塞进了队尾。这样就可以在处理小任务的间隙处理用户操作。

进程指示

OK!我们现在补充一个原理: 在宏任务执行的时候,浏览器不做任何的渲染,不管这个宏任务执行的时间的长短。

有个这个理解后,我们在说说我们还可以做点什么?有时候我们想把一个任务的执行过程显示出来。怎么做呢?

<div id="progress"></div>
<script>
function count() {
for (let i = 0; i < 1e6; i++) {
i++;
progress.innerHTML = i;
}
}
count();
</script>
复制代码

希望读者可以自己跑一下,你会发现你只能看到最后的结果999999. 而如果我们这样做了后。

<div id="progress"></div>
<script>
let i = 0;
function count() {
do {
i++;
progress.innerHTML = i;
} while (i % 1e3 != 0);
if (i < 1e7) {
setTimeout(count);
}
}
count();
</script>
复制代码

你会看到一个数字不断地加1000地上升。

微任务与宏任务

讲完了宏任务,来讲讲微任务。微任务是由Promise创建出来的且js中有一个专门的微任务队列来存储微任务。微任务的机制是:当执行完一个任务后,只要有微任务就先执行微任务。宏任务和渲染通通排到后面。

我们来看一段代码。

setTimeout(() => console.log("timeout"));
Promise.resolve()
.then(() => console.log("promise"));
console.log("code");
复制代码

假设这是一个脚本文件,首先将setTimeout的回调塞入到宏任务队列中,但是因为这个脚本文件还没执行完,所以它不会立刻得到执行。然后将then中的函数塞到微任务队列中,然后输出了’code’,这个脚本文件宏任务执行完了。因为微任务的存在,js引擎先执行微任务,所以先输出’promise’,这样最后输出’timeout’。

所以最后的输出顺序是 code promise timeout.

微信扫一扫,分享到朋友圈

浅聊事件循环:宏任务和微任务

Vue编辑&新建表单复用的一些思考

上一篇

Koa2启动过程都做了什么-源码读阅

下一篇

你也可能喜欢

浅聊事件循环:宏任务和微任务

长按储存图像,分享给朋友