DEV Community

Cover image for JavaScript: Microtasks e Macrotasks
Eduardo Rabelo
Eduardo Rabelo

Posted on Edited on

JavaScript: Microtasks e Macrotasks

Duas histórias no mesmo Event Loop e suas prioridades

Dentro do Event Loop do JavaScript/Node.js, existem dois tipos de tarefas com prioridades bem diferentes, por exemplo:

  • Microtasks: process.nextTick, Promises, Object.observe, MutationObserver
  • Macrotasks: setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering

Mas qual a diferença prática entre os dois? Podemos resumir na seguinte afirmação:

Se alguma Microtask estiver pendente na fila para o Event Loop, ela será executada antes do início do próximo loop (no final do loop atual). Já as Macrotask serão executadas apenas no próximo loop.

Essa definição é importante, pois o resultado de algumas operações podem surpreender os não familiarizados com a engine do JavaScript.

Com isso em mente, qual será o resultado do trecho de código abaixo?

console.log('01-Console');

setTimeout(_ => console.log('02-Timeout'), 0);

Promise.resolve().then(_ => console.log('03-Promise'));

console.log('04-Console');
Enter fullscreen mode Exit fullscreen mode

Antes de olhar a resposta abaixo, você consegue imaginar a saída desse programa?


Uma hora ou outra precisamos saber a resposta…

Ao executar o trecho acima, teremos o seguinte resultado:

$ node tasks.js

01-Console
04-Console
03-Promise
02-Timeout
Enter fullscreen mode Exit fullscreen mode

Colocando algumas anotações no código, temos:

console.log('01-Console'); // [A]

setTimeout(_ => console.log('02-Timeout'), 0); // [B]

Promise.resolve().then(_ => console.log('03-Promise')); // [C]

console.log('04-Console'); // [D]
Enter fullscreen mode Exit fullscreen mode
  • [A]: Executado diretamente na “main thread”, síncrono
  • [B]: Enfileirado como uma tarefa futura, prioridade “macrotask”, será executado apenas no próximo loop
  • [C]: Enfileirado como uma tarefa futura, prioridade “microtask”, será executado imediatamente após todas as tarefas/tasks do loop atual e antes do próximo loop
  • [D]: Executado diretamente na “main thread”, síncrono

📚 Links Relacionados

Top comments (5)

Collapse
 
alexparra profile image
Alex Parra •

Nice heads up.

Collapse
 
gustavolopes95 profile image
GustavoLopes95 •

Nice post

Collapse
 
marcos1305 profile image
Marcos Vinicius •

Muito bom!!

Collapse
 
cleiton55048426 profile image
Cleiton •

Ótimo post Eduardo. Muito bom poder encontrar conteúdo de qualidade e em português aqui no dev.

Sendo o callback uma forma de ancestral das promisses, ele se encaixaria como uma microtask, certo?

Collapse
 
oieduardorabelo profile image
Eduardo Rabelo •

fala Cleiton! muito obrigado pelas palavras, eu costumo postar uma vez por semana aqui e no meu Medium (link no profile),

Sendo o callback uma forma de ancestral das promisses, ele se encaixaria como uma microtask, certo?

se usarmos ES5 como a "forma ancestral", nós não tínhamos o objeto "Promise" naquele tempo, boa parte de toda tarefa tardia de bibliotecas como jQuery, Angular 1, Backbone, etc, usavam alguma implementação com setTimeout/setInterval ou alguma outra mágica. Isso significa que se encaixam na categoria de Macrotask.

agora, você precisa entender/olhar a implementação do callback, pois o seguinte é válido:

function fazAlgo(callback) {
fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => response.json())
  .then(json => callback(json))
}

fazAlgo(json => {
  // isso é um callback rodando em microtask
})

no exemplo acima, temos um callback sendo executado dentro de uma promise, que por spec é microtask :P

o caótico mundo do JS! :D