Comment mettre en file d'attente une microtâche si le navigateur ne prend pas en charge les promesses natives?

11

Il est préférable d'écrire du code qui ne dépend pas de la synchronisation des rappels immédiats (comme les microtâches contre les macrotâches), mais mettons cela de côté pour le moment.

setTimeoutmet en file d'attente une macrotâche, qui, au minimum, attend de démarrer jusqu'à ce que tous les microtâches (et les microtâches qu'ils génèrent) se terminent. Voici un exemple:

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

Le comportement d'un .thensur une promesse résolue est fondamentalement différent du comportement d'un setTimeoutrappel immédiat - la promesse .thens'exécutera en premier, même si le a setTimeoutété mis en file d'attente en premier. Mais seuls les navigateurs modernes prennent en charge Promises. Comment la fonctionnalité spéciale d'une microtâche peut-elle être correctement remplie si elle Promisen'existe pas?

Si vous essayez d'imiter une .thenmicrotâche en utilisant un setTimeout, vous allez mettre en file d'attente une macrotâche, pas une microtâche, de sorte que la mauvaise polyfonction .thenne fonctionnera pas au bon moment si une macrotâche est déjà en file d'attente.

Il y a une solution à utiliser MutationObserver, mais elle a l'air moche, et ce n'est pas MutationObserverpour ça . En outre, MutationObservern'est pas pris en charge sur IE10 et versions antérieures. Si l'on veut mettre en file d'attente une microtâche dans un environnement qui ne prend pas en charge nativement Promises, existe-t-il de meilleures alternatives?

(Je n'essaie pas réellement de prendre en charge IE10 - ce n'est qu'un exercice théorique sur la façon dont les microtâches peuvent être mises en file d'attente sans promesses)

Neige
la source
1
Je suggère de jeter un œil aux implémentations prometteuses qui sont axées sur les performances, en particulier Bluebird. Un coup d'œil à son histoireschedule.js sera instructif.
Bergi
Avez-vous essayé de polyfiler la promesse en utilisant quelque chose comme core-js?
Hugo

Réponses:

4

Si nous parlons d'IE, vous pouvez utiliser setImmediate

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

En outre, MutationObserver n'est pas pris en charge sur IE10 et versions antérieures.

setImmediateest pris en charge sur IE10. Donc, plus une version IE.
Et, si vous êtes intéressé, plus Node.js.

Il existe une solution utilisant MutationObserver, mais elle a l'air moche et ce n'est pas à cela que sert MutationObserver.

Il existe d'autres polyfills possibles, voici quelques implémentations: https://github.com/YuzuJS/setImmediate/blob/master/setImmediate.js (celui-ci est mentionné dans MDN) https://github.com/taylorhakes/ setAsap / blob / master / setAsap.js (un plus simple)

Et comme presque tous les polyfills, ils sont aussi laids.

Mais de toute façon, voici un exemple dans son essence (en utilisant postMessage), et je pense qu'il est le moins laid de tous (mais aussi pas un vrai 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
la source
De super trouvailles, je les aime toutes!
Snow
@Snow, au fait, vous avez dit que c'était un exercice théorique, mais, je suis toujours curieux, comment avez-vous trouvé cette idée en 2019?
x00
Je me demandais simplement comment les microtâches pouvaient être mises en file d'attente, il n'y avait vraiment rien de plus spécifique. J'espérais en quelque sorte qu'il y avait quelque chose dans le langage qui leur permettait d'y accéder, autre que Promises, mais il semble que non. Toutes les autres méthodes cherchent à impliquer l' invocation bizarreries environnement spécifiques qui ne sont pas conçus pour ce genre de chose (mais juste arriver au travail de toute façon).
Neige le
8

J'ai vu que les mutationObserverrappels utilisent des microtâches, et heureusement, IE11 le prend en charge, j'ai donc eu l'idée de mettre en file d'attente une microtâche dans IE11 en enregistrant le rappel et en déclenchant immédiatement l'observateur en changeant un élément:

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

Vous pouvez ouvrir IE11 et voir le fonctionnement ci-dessus, mais le code semble étrange.

Neige
la source