如果浏览器不支持原生 Promise,如何将微任务排队?

Sno*_*now 11 javascript event-loop promise

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

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');
Run Code Online (Sandbox Code Playgroud)

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

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

有一个使用 的解决方案MutationObserver,但它看起来很难看,而且不是MutationObserver用来做什么的。此外,MutationObserver在 IE10 及更早版本上不支持。如果想要在原生不支持 Promises 的环境中排队一个微任务,有没有更好的选择?

(我实际上并不是尝试支持 IE10 - 这只是关于微任务如何在没有 Promise 的情况下排队的理论练习)

Sno*_*now 8

我看到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');
Run Code Online (Sandbox Code Playgroud)

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


x00*_*x00 4

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

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

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

setImmediateIE10 支持。所以再加一个IE版本。
而且,如果您有兴趣,请加上 Node.js。

有一个使用 MutationObserver 的解决方案,但它看起来很丑,而且不是 MutationObserver 的用途。

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

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

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

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');
Run Code Online (Sandbox Code Playgroud)