如果浏览器不支持原生承诺,如何对微任务进行排队?



最好编写不依赖于即时回调时间的代码(如微任务与宏任务(,但让我们暂时将其放在一边。

setTimeout将宏任务排队,该宏任务至少要等到所有微任务(以及它们生成的微任务(完成后才能开始。下面是一个示例:

console.log('Macrotask queued');
setTimeout(function() {
console.log('Macrotask running');
});
Promise.resolve()
.then(function() {
console.log('Microtask running');
});
console.log('Microtask queued');
console.log('Last line of script');

已解决的承诺上的.then行为与即时setTimeout回调的行为根本不同 - 承诺.then将首先运行,即使setTimeout首先排队也是如此。但只有现代浏览器支持承诺。如果微任务的特殊功能不存在Promise如何正确填充?

如果您尝试使用setTimeout来模仿.then的微任务,您将排队一个宏任务,而不是微任务,因此如果宏任务已经排队,填充不良的.then将不会在正确的时间运行。

有一个使用MutationObserver的解决方案,但它看起来很丑,不是MutationObserver的目的。此外,IE10 及更早版本不支持MutationObserver。如果一个人想在原生不支持 Promise 的环境中对微任务进行排队,有没有更好的选择?

(我实际上并没有试图支持IE10 - 这只是一个关于如何在没有承诺的情况下排队的微任务的理论练习(

我看到mutationObserver回调使用微任务,幸运的是,IE11 支持它,所以我想到了在 IE11 中通过保存回调来排队微任务,然后通过更改元素立即触发观察者:

var weirdQueueMicrotask = (function() {
var elementThatChanges = document.createElement('div');
var callback;
var bool = false;
new MutationObserver(function() {
callback();
}).observe(elementThatChanges, { childList: true });
return function(callbackParam) {
callback = callbackParam;
elementThatChanges.textContent = bool = !bool;
};
})();
setTimeout(function() {
console.log('Macrotask running');
});
console.log('Macrotask queued');
weirdQueueMicrotask(function() {
console.log('Microtask running');
});
console.log('Microtask queued');
console.log('Last line of script');

您可以打开IE11并看到上面的工作,但是代码看起来很奇怪。

如果我们谈论的是IE,您可以使用setImmediate

https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate

此外,IE10 及更早版本不支持 MutationObserver 。

IE10 支持setImmediate。所以加上一个IE版本。
而且,如果您有兴趣,还可以添加 Node.js。

有一个使用MutationObserver

的解决方案,但它看起来很丑,不是MutationObserver的目的。

还有其他可能的 polyfill,这里有几个实现: https://github.com/YuzuJS/setImmediate/blob/master/setImmediate.js(这个在MDN中提到( https://github.com/taylorhakes/setAsap/blob/master/setAsap.js(更简单的(

和几乎所有的填充物一样,它们也很丑陋。

但无论如何,这里有一个本质上的例子(使用 postMessage(,我认为它是最不丑陋的(但也不是真正的 polyfill(

var setImmediate = (function() {
var queue = [];
function on_message(e) {
if(e.data === "setImmediateMsg") queue.pop()()
}
if(window.addEventListener) { // IE9+
window.addEventListener('message', on_message)
} else { // IE8
window.attachEvent('onmessage', on_message)
}
return function(fn) {
queue.unshift(fn)
window.postMessage("setImmediateMsg", "*")
}
}())
setTimeout(function() {
console.log('Macrotask running');
});
console.log('Macrotask queued');
setImmediate(function() {
console.log('Microtask running');
});
console.log('Microtask queued');
console.log('Last line of script');

最新更新