介绍

今天闲来无聊,偶然看到个api – queueMicrotask,其MDN地址为queueMicrotask,觉得挺有意思的,于是想写一篇文章来跟大家分享一下。


用法

首先,这个api看字面意思就知道和微任务有关,至于js中的宏任务和微任务,我就不多说了,默认大家都知道这些东西,不了解的读者可以去查阅相关的资料。

1
queueMicrotask(function() {/* ... */})

可以看到,此api需要传入一个回调函数作为参数,然后以微任务的形式执行此回调函数,换言之,就是把此回调函数放入此次js事件循环的微任务队列中。

我们不妨来看几个例子:
例子1:

1
2
3
4
5
6
7
8
9
10
console.log(111)
setTimeout(() => {
console.log(222)
})

queueMicrotask(() => {
console.log(333)
})

console.log(444)

那么,此例子中的代码执行结果是什么呢?

1
2
3
4
111
444
333
222

你猜对了吗?


兼容性

详情见queueMicrotask


queueMicrotask的polyfill

如果当前环境不支持queueMicrotask,则可以像下面这样模拟:

1.MDN

1
2
3
4
5
6
7
if (typeof queueMicrotask !== "function") {
queueMicrotask = function (callback) {
Promise.resolve()
.then(callback)
.catch(e => setTimeout(() => { throw e; }));
};
}

2.core-js

相比较 MDN 的实现,core-js 会复杂一些,它同时考虑了 nodejs 和 browser 两种情况,同时利用链表数据结构来模拟微任务队列的执行单元,同时实现了一个 flush 方法表示执行全部的微任务单元。

还实现了一个 notify 方法,该方法会根据具体的 js 运行时环境以及 api 的支持情况,分别尝试使用 process.nextTick、MutationObserver 和 Promise.resolve 以及最基本的宏任务 api 来执行 flush方法,变相模拟微任务的执行过程。


为什么我们需要这个 api?

从微任务本身的概念来说的话,就是当我们期望某段代码,不阻塞当前执行的同步代码,同时又期望它尽可能快地执行时,我们就需要它。由于它是一个用于指派微任务的底层 api,我们很可能会在其中无限制地指派微任务到其队列之中,这样做的效果就是,浏览器的微任务队列始终处于非空状态,这将导致控制权始终无法交还给浏览器进行下一次事件循环,需要注意的是,不要长时间连续使用此api(比如递归),否则js的执行将一直在做添加微任务和清空微任务队列的工作,最终导致浏览器卡死,比如下面这样:

1
2
3
4
5
function infiniteFn() {
queueMicrotask(infiniteFn)
}

queueMicrotask(infiniteFn)