Diferença entre microtask e macrotask em um contexto de loop de events

Acabei de ler a especificação Promises / A + e deparei com os termos microtask e macrotask: consulte http://promisesaplus.com/#notes

Eu nunca ouvi falar desses termos antes, e agora estou curioso sobre qual seria a diferença?

Eu já tentei encontrar algumas informações na web, mas tudo que eu encontrei é este post dos arquivos w3.org (que não explica a diferença para mim): http://lists.w3.org/Archives /Public/public-nextweb/2013Jul/0018.html

Além disso, encontrei um módulo npm chamado “macrotask”: https://www.npmjs.org/package/macrotask Novamente, não está claro qual é a diferença.

Tudo o que sei é que isso tem algo a ver com o loop de events, conforme descrito em https://html.spec.whatwg.org/multipage/webappapis.html#task-queue e https: //html.spec.whatwg .org / multipage / webappapis.html # perform-a-microtask-checkpoint

Eu sei que teoricamente eu seria capaz de extrair as diferenças sozinho, dada a especificação WHATWG. Mas tenho certeza de que outros poderiam se beneficiar também de uma breve explicação dada por um especialista.

Uma volta do loop de events terá exatamente uma tarefa sendo processada a partir da fila macrotsk (essa fila é simplesmente chamada de fila de tarefas na especificação WHATWG ). Depois que esta macrotarefa terminar, todas as microtarefas disponíveis serão processadas, ou seja, dentro do mesmo ciclo circular. Enquanto essas microtarefas são processadas, elas podem enfileirar ainda mais microtarefas, que serão todas executadas uma por uma, até que a fila de microtarefas esteja esgotada.

Quais são as conseqüências práticas disso?

Se uma microtarefa enfileirar recursivamente outras microtarefas, poderá levar um longo tempo até que a próxima macrotarefa seja processada. Isso significa que você pode acabar com uma interface do usuário bloqueada ou com uma inatividade de E / S acabada em seu aplicativo.

No entanto, pelo menos no que diz respeito à function process.nextTick do Node.js (que enfileira as microtarefas ), há uma proteção embutida contra esse bloqueio por meio do process.maxTickDepth. Este valor é definido como um padrão de 1000, reduzindo o processamento adicional de microtarefas após esse limite ser atingido, permitindo que a próxima macrotarefa seja processada)

Então, quando usar o que?

Basicamente, use microtarefas quando você precisar fazer coisas de forma assíncrona de forma síncrona (ou seja, quando você diria executar essa (micro) tarefa no futuro mais imediato ). Caso contrário, fique com macrotarefas .

Exemplos

macrotasks: setTimeout, setInterval, setImmediate, requestAnimationFrame, E / S, renderização da interface do usuário
microtasks: process.nextTick, Promises, Object.observe, MutationObserver

Eu escrevi um post sobre isso, incluindo exemplos interativos https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

Atualização: também fiz uma palestra sobre este https://www.youtube.com/watch?v=cCOL7MC4Pl0 . A conversa entra em mais detalhes, incluindo como as tarefas e microtarefas interagem com a renderização.

Conceitos básicos em especificação :

  • Um loop de events tem uma ou mais filas de tarefas (a fila de tarefas é uma fila de macrotarefas)
  • Cada loop de events tem uma fila de microtask.
  • task queue = fila macrotsk! = fila microtsk
  • uma tarefa pode ser enviada para a fila do macrotsk ou para a fila de microtask
  • quando uma tarefa é colocada em uma fila (micro / macro), queremos dizer que o trabalho de preparação está concluído, então a tarefa pode ser executada agora.

E o modelo de processo de loop de events é o seguinte:

quando a pilha de chamadas estiver vazia, siga os passos-

  1. selecione a tarefa mais antiga (tarefa A) nas filas de tarefas
  2. se a tarefa A for nula (significa que as filas de tarefas estão vazias), salte para o passo 6
  3. definir “tarefa em execução no momento” para “tarefa A”
  4. execute “tarefa A” (significa executar a function de retorno de chamada)
  5. set “current running task” para null, remove “task A”
  6. realizar fila de microtask
    • (a) .selecione a tarefa mais antiga (tarefa x) na fila de microtarefas
    • (b) .se a tarefa x é nula (significa que as filas microtarefas estão vazias), pule para o passo (g)
    • (c) .set “tarefa atualmente em execução” para “tarefa x”
    • (d) “tarefa x”
    • (e) .set “tarefa atualmente em execução” para null, remover “tarefa x”
    • (f) .selecione a próxima tarefa mais antiga na fila de microtarefas, pule para a etapa (b)
    • (g) fila de microtarefa final;
  7. pule para o passo 1.

um modelo de processo simplificado é o seguinte:

  1. execute a tarefa mais antiga na fila macrotsk e, em seguida, remova-a.
  2. execute todas as tarefas disponíveis na fila de microtask e, em seguida, remova-as.
  3. próxima rodada: execute a próxima tarefa na fila de macrotarefas (etapa 2 de salto)

algo para lembrar:

  1. Quando uma tarefa (na fila do macrotask) está em execução, novos events podem ser registrados. Assim, novas tarefas podem ser criadas. Abaixo estão duas novas tarefas criadas:
    • O callback de promiseA.then () é uma tarefa
      • O promiseA é resolvido / rejeitado: a tarefa será enviada para a fila microtsk na rodada atual do loop de events.
      • promiseA está pendente: a tarefa será enviada para a fila microtsk na próxima rodada de loop de events (pode ser a próxima rodada)
    • O callback setTimeout (callback, n) é uma tarefa, e será colocado na fila do macrotsk, mesmo n é 0;
  2. A tarefa na fila de microtask será executada na rodada atual, enquanto a tarefa na fila de macrotarefas deve aguardar a próxima rodada do loop de events.
  3. Todos nós sabemos que o retorno de “click”, “scroll”, “ajax”, “setTimeout” … são tarefas, no entanto, devemos lembrar também que os códigos js como um todo na tag script são uma tarefa (uma macrotarefa) também.