如果浏览器不支持本机 Promises,如何对微任务进行排队?

How to queue a microtask if the browser doesn't support native Promises?

最好编写不依赖于即时回调时间的代码(例如微任务与宏任务),但我们暂时搁置它。

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 对已解决 Promise 的行为与立即 setTimeout 回调的行为根本不同 - Promise .then 将首先 运行,甚至如果 setTimeout 先排队。但只有现代浏览器支持 Promises。如果 Promise 不存在,如何正确填充微任务的 special 功能?

如果你试图通过使用 setTimeout 来模仿 .then 的微任务,你将排队一个宏任务,而不是微任务,所以 polyfilled 糟糕的 .then 赢了't 运行 如果一个宏任务已经排队。

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

(我并不是 实际上 试图支持 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

Also, MutationObserver is not supported on IE10 and earlier.

IE10 支持

setImmediate。所以加一个IE版本。
并且,如果您有兴趣,请加 Node.js.

There's a solution using MutationObserver, but it looks ugly, and isn't what MutationObserver is for.

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

和几乎所有的 polyfill 一样,它们也很丑。

但无论如何,这是一个本质上的例子(使用 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');