Как поставить микрозадачу в очередь, если браузер не поддерживает встроенные обещания?

11

Лучше написать код, который не зависит от времени немедленных обратных вызовов (например, от микрозадач против макрозадач), но давайте пока отложим это.

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

Поведение a .thenдля разрешенного Promise принципиально отличается от поведения немедленного setTimeoutобратного вызова - Promise .thenбудет запущен первым, даже если setTimeoutсначала был поставлен в очередь. Но только современные браузеры поддерживают Promises. Как специальные функции микрозадачи могут быть правильно заполнены, если Promiseих не существует?

Если вы попытаетесь имитировать .thenмикрозадачу с помощью setTimeout, вы будете ставить в очередь макрозадачу, а не микрозадачу, поэтому плохо заполненный .thenне запустится в нужное время, если макрозадача уже поставлена ​​в очередь.

Есть решение, использующее MutationObserver, но оно выглядит некрасиво и не для чего MutationObserver. Кроме того, MutationObserverне поддерживается в IE10 и более ранних версиях. Если кто-то хочет поставить микрозадачу в очередь в среде, которая изначально не поддерживает обещания, есть ли лучшие альтернативы?

(На самом деле я не пытаюсь поддерживать IE10 - это всего лишь теоретическое упражнение о том, как микротрубы можно ставить в очередь без обещаний)

Снег
источник
1
Я бы посоветовал взглянуть на реализации обещаний, которые ориентированы на производительность, особенно Bluebird. Взглянуть на историю егоschedule.js будет поучительно.
Берги
Вы пробовали полифилировать Promise, используя что-то вроде core-js?
Хьюго

Ответы:

4

Если мы говорим об IE, вы можете использовать setImmediate

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

Кроме того, MutationObserver не поддерживается в IE10 и более ранних версиях.

setImmediateподдерживается на IE10. Итак, плюс одна версия IE.
И, если вы заинтересованы, плюс Node.js.

Есть решение, использующее MutationObserver, но оно выглядит некрасиво и не для чего предназначен MutationObserver.

Существуют и другие возможные заполнения, вот несколько реализаций: https://github.com/YuzuJS/setImmediate/blob/master/setImmediate.js (эта упоминается в MDN) https://github.com/taylorhakes/ setAsap / blob / master / setAsap.js (более простой)

И как почти все полифиллы, они тоже безобразны.

Но в любом случае, вот пример по своей сути (с использованием 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');

x00
источник
Великолепные находки, они мне нравятся все!
Снег
@ Кстати, вы сказали, что это теоретическое упражнение, но мне все еще интересно, как вы столкнулись с этой идеей в 2019 году?
x00
Мне было просто интересно, как можно поставить микрозадачи в очередь, на самом деле не было ничего более конкретного. Я надеялся, что в языке есть что-то, что обеспечивает доступ к ним, кроме Обещаний, но похоже, что нет. Все остальные методы выглядят привлекать применение экологически конкретные причуд , которые не были предназначены для такого рода вещей (но только случаются работать в любом случае).
снег
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');

Вы можете открыть IE11 и увидеть, как работает выше, но код выглядит странно.

Снег
источник