console.log('1.Start');
let flag = true;
function foo(fn) {
console.log('2.Function foo is on');
if (flag) {
setTimeout(fn);
flag = false;
} else {
// queueMicrotask()创建一个微任务
// 效果和使用Promise一样的,都是将任务加入微任务队列
// 待宏任务结束后依次执行
queueMicrotask(fn);
// Promise.resolve().then(fn);
}
}
foo(() => {
console.log('3.setTimeout is on');
});
foo(() => {
console.log('4.Promise is on');
});
console.log('5.End');
// 完整输出
// 1.Start
// 2.Function foo is on
// 2.Function foo is on
// 5.End
// 4.Promise is on
// 3.setTimeout is on
因为微任务自身可以入列更多的微任务,且事件循环会持续处理微任务直至队列为空,那么就存在一种使得事件循环无尽处理微任务的真实风险。如何处理递归增加微任务是要谨慎而行的。
如果可能的话,大部分开发者并不应该过多的使用微任务。在基于现代浏览器的 JavaScript 开发中有一个高度专业化的特性,那就是允许你调度代码跳转到其他事情之前,而那些事情原本是处于用户计算机中一大堆等待发生的事情集合之中的。滥用这种能力将带来性能问题。
通过引入 queueMicrotask(),可以避免通过 promise 去创建微任务而带来的风险。举例来说,当使用 promise 创建微任务时,由回调抛出的异常被报告为 rejected promises 而不是标准异常。同时,创建和销毁 promise 带来了事件和内存方面的额外开销,这是正确入列微任务的函数应该避免的。