Como enfileirar uma microtask se o navegador não suporta promessas nativas?

11

É melhor escrever um código que não dependa do tempo de retornos de chamada imediatos (como microtasks vs macrotasks), mas vamos deixar isso de lado por enquanto.

setTimeoutenfileira uma macrotask, que, no mínimo, espera para iniciar até que todas as microtasks (e microtasks que elas geram) terminem. Aqui está um exemplo:

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

O comportamento de uma .thenPromessa resolvida é fundamentalmente diferente do comportamento de um setTimeoutretorno de chamada imediato - a Promessa .thenserá executada primeiro, mesmo que tenha setTimeoutsido colocada na fila primeiro. Mas apenas navegadores modernos suportam promessas. Como a funcionalidade especial de uma microtask pode ser preenchida corretamente se Promisenão existe?

Se você tentar imitar uma .thenmicrotask de uma usando setTimeout, você estará enfileirando uma macrotask, não uma microtask, para que o mal preenchido com polietileno .thennão seja executado no momento certo se uma macrotask já estiver na fila.

Existe uma solução em uso MutationObserver, mas parece feia e não MutationObserveré para isso. Além disso, MutationObservernão é suportado no IE10 e versões anteriores. Se alguém deseja enfileirar uma microtask em um ambiente que não oferece suporte nativo ao Promises, existem alternativas melhores?

(Na verdade, não estou tentando oferecer suporte ao IE10 - este é apenas um exercício teórico sobre como as microtasks podem ser enfileiradas sem promessas)

Neve
fonte
11
Eu sugeriria dar uma olhada nas implementações promissoras que são orientadas para o desempenho, especialmente o Bluebird. Dar uma olhada na história delaschedule.js será esclarecedor.
Bergi 7/01
Você já tentou polyfiling the Promise usando algo como core-js?
Hugo

Respostas:

4

Se estamos falando sobre o IE, você pode usar setImmediate

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

Além disso, MutationObserver não é suportado no IE10 e versões anteriores.

setImmediateé suportado no IE10. Então, mais uma versão do IE.
E, se você estiver interessado, mais Node.js.

Existe uma solução usando o MutationObserver, mas parece feia e não é para isso que serve o MutationObserver.

Existem outros polyfills possíveis, eis algumas implementações: https://github.com/YuzuJS/setImmediate/blob/master/setImmediate.js (este é mencionado no MDN) https://github.com/taylorhakes/ setAsap / blob / master / setAsap.js (mais simples)

E como quase todos os polyfills, eles também são feios.

Mas de qualquer maneira, aqui está um exemplo em sua essência (usando postMessage), e acho que é menos feio de todos (mas também não é um verdadeiro 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
fonte
Grandes achados, eu gosto de todos eles!
Snow
@ Snow, a propósito, você disse que é um exercício teórico, mas ainda estou curioso, como você se deparou com essa ideia em 2019?
x00 15/01
Eu só estava me perguntando como microtasks poderiam ser enfileiradas, realmente não havia nada mais específico. Eu meio que esperava que houvesse algo embutido na linguagem que lhes desse acesso, além de Promises, mas parece que não existe. Todos os outros métodos de olhar para envolver invocando peculiaridades específicas do ambiente que não foram projetados para esse tipo de coisa (mas apenas acontecer ao trabalho de qualquer maneira).
Snow
8

Vi que os mutationObserverretornos de chamada usam microtasks e, felizmente, o IE11 suporta, então tive a ideia de enfileirar um microtask no IE11 salvando o retorno de chamada e acionando imediatamente o observador alterando um 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');

Você pode abrir o IE11 e ver o trabalho acima, mas o código parece estranho.

Neve
fonte