¿Cómo poner en cola un microtask si el navegador no admite Promesas nativas?

11

Es mejor escribir código que no se base en el momento de las devoluciones de llamadas inmediatas (como microtasks vs macrotasks), pero dejemos eso de lado por el momento.

setTimeoutpone en cola un macrotask, que, como mínimo, espera comenzar hasta que finalicen todos los microtasks (y los microtasks que generan). Aquí hay un ejemplo:

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

El comportamiento de una .thenPromesa resuelta es fundamentalmente diferente del comportamiento de una setTimeoutdevolución de llamada inmediata : la Promesa .thense ejecutará primero, incluso si setTimeoutprimero se puso en cola. Pero solo los navegadores modernos admiten Promesas. ¿Cómo puede la funcionalidad especial de un microtask ser debidamente rellenada si Promiseno existe?

Si intentas imitar la microtask de un usuario .thenusando setTimeout, estarás haciendo cola con una macrotask, no con una microtask, por lo que el mal polifundido .thenno se ejecutará en el momento adecuado si una macrotask ya está en cola.

Hay una solución que usa MutationObserver, pero se ve fea y no MutationObserveres para lo que sirve. Además, MutationObserverno es compatible con IE10 y versiones anteriores. Si se quiere poner en cola una microtask en un entorno que no admite Promesas de forma nativa, ¿hay alguna alternativa mejor?

(En realidad, no estoy tratando de admitir IE10; este es solo un ejercicio teórico sobre cómo se pueden poner en cola las microtask sin Promesas)

Nieve
fuente
1
Sugeriría echar un vistazo a las implementaciones prometedoras que están orientadas al rendimiento, especialmente Bluebird. Echar un vistazo a su historiaschedule.js será esclarecedor.
Bergi
¿Has intentado polyfiling the Promise usando algo como core-js?
Hugo

Respuestas:

4

Si estamos hablando de IE puedes usar setImmediate

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

Además, MutationObserver no es compatible con IE10 y versiones anteriores.

setImmediatees compatible con IE10. Entonces, más una versión de IE.
Y, si está interesado, más Node.js.

Hay una solución que usa MutationObserver, pero se ve fea y no es para lo que MutationObserver es.

Hay otros posibles polyfills, aquí hay un par de implementaciones: https://github.com/YuzuJS/setImmediate/blob/master/setImmediate.js (este se menciona en MDN) https://github.com/taylorhakes/ setAsap / blob / master / setAsap.js (uno más simple)

Y como casi todos los polyfills son feos también.

Pero de todos modos, aquí hay un ejemplo en su esencia (usando postMessage), y creo que es el menos feo de todos (pero tampoco es un verdadero 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');

x00
fuente
Grandes hallazgos, me gustan todos!
Nieve
@Snow, por cierto, dijiste que es un ejercicio teórico, pero, todavía tengo curiosidad, ¿cómo se te ocurrió esta idea en 2019?
x00
Me preguntaba cómo se podían poner en cola los microtasks, realmente no había nada más específico. Tenía la esperanza de que hubiera algo integrado en el lenguaje que proporcionara acceso a ellos, aparte de Promesas, pero parece que no existe. Todos los otros métodos parecen implicar invocando peculiaridades entorno específicos que no fueron diseñados para ese tipo de cosas (pero sólo sucede con el trabajo de todos modos).
Nieve
8

Vi que las mutationObserverdevoluciones de llamada usan microtask, y afortunadamente, IE11 lo admite, así que tuve la idea de poner en cola una microtask en IE11 guardando la devolución de llamada y luego disparando inmediatamente al observador cambiando un elemento:

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

Puede abrir IE11 y ver el funcionamiento anterior, pero el código parece extraño.

Nieve
fuente