最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to queue a microtask if the browser doesn't support native Promises? - Stack Overflow

programmeradmin1浏览0评论

It's better to write code that doesn't rely on the timing of immediate callbacks (like microtasks vs macrotasks), but let's put that aside for the moment.

setTimeout queues a macrotask, which, at a minimum, waits to start until all microtasks (and microtasks that they spawn) finish. Here's an example:

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');

It's better to write code that doesn't rely on the timing of immediate callbacks (like microtasks vs macrotasks), but let's put that aside for the moment.

setTimeout queues a macrotask, which, at a minimum, waits to start until all microtasks (and microtasks that they spawn) finish. Here's an example:

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');

The behavior of a .then on a resolved Promise is fundamentally different from the behavior of an immediate setTimeout callback - the Promise .then will run first, even if the setTimeout was queued first. But only modern browsers support Promises. How can the special functionality of a microtask be properly polyfilled if Promise doesn't exist?

If you try to imitate a .then's microtask by using setTimeout, you'll be queuing a macrotask, not a microtask, so the badly-polyfilled .then won't run at the right time if a macrotask is already queued.

There's a solution using MutationObserver, but it looks ugly, and isn't what MutationObserver is for. Also, MutationObserver is not supported on IE10 and earlier. If one wants to queue a microtask in an environment that doesn't natively support Promises, are there any better alternatives?

(I'm not actually trying to support IE10 - this is just a theoretical exercise on how microtasks can be queued without Promises)

Share Improve this question edited Jan 14, 2020 at 7:53 Snow asked Dec 18, 2019 at 11:50 SnowSnow 4,1183 gold badges16 silver badges43 bronze badges 2
  • 1 I would suggest having a look at promise implementations that are performance-oriented, especially Bluebird. Taking a look at the history of its schedule.js will be enlightening. – Bergi Commented Jan 7, 2020 at 23:27
  • Have you tried polyfiling the Promise using something like core-js? – Hugo Commented Jan 13, 2020 at 16:59
Add a ment  | 

2 Answers 2

Reset to default 8

I saw that mutationObserver callbacks use microtasks, and luckily, IE11 supports it, so I had the idea to queue a microtask in IE11 by saving the callback and then immediately triggering the observer by changing an element:

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');

You can open up IE11 and see the above working, but the code looks strange.

If we are talking about IE you can use setImmediate

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

Also, MutationObserver is not supported on IE10 and earlier.

setImmediate is supported on IE10. So plus one IE version.
And, if your are interested, plus Node.js.

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

There are other possible polyfills, here is a couple of implementations: https://github./YuzuJS/setImmediate/blob/master/setImmediate.js (this one is mentioned in MDN) https://github./taylorhakes/setAsap/blob/master/setAsap.js (a more simple one)

And as almost all polyfills they are ugly as well.

But anyway, here is an example in its essence (using postMessage), and I think it is least ugly of all (but also not a true 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');

发布评论

评论列表(0)

  1. 暂无评论