Node.js: Programação Assíncrona

Última atualização: 2026-08-26

1. Relato: Um serviço bloqueado por 200 ms

O serviço de API do Bob normalmente responde em apenas 5 ms, mas durante os horários de pico, os tempos de resposta dispararam para 2.000 ms. Os usuários não paravam de reclamar e, a princípio, ele achou que o banco de dados estava lento, mas adicionar índices não adiantou. Mais tarde, usando o console.time para solucionar o problema passo a passo, ele descobriu que a classificação de um grande array JSON síncrono estava levando 200 ms — essa operação de 200 ms estava bloqueando o loop de eventos, fazendo com que todas as solicitações na fila ficassem aguardando sua conclusão. Depois de entender como o loop de eventos funciona, Bob usou setImmediate para dividir a classificação em partes menores e executá-las uma a uma, reduzindo os tempos de resposta durante os horários de pico para 50 ms.

Você vai aprender:



2. As 6 etapas do ciclo de eventos

O ciclo de eventos do Node.js é controlado pela libuv e passa por seis fases em sequência. Cada ciclo começa com os “temporizadores” e termina com os “callbacks de encerramento”, retornando então aos “temporizadores” para iniciar o próximo ciclo.

100%
flowchart TD
    A["timers<br/>setTimeout / setInterval"] --> B["pending callbacks<br/>System-Level Callbacks"]
    B --> C["idle, prepare<br/>For internal use only"]
    C --> D["poll<br/>I/O callbacks / new I/O polling"]
    D --> E["check<br/>setImmediate"]
    E --> F["close callbacks<br/>socket.on('close')"]
    F --> A
Fase Tipo de callback Descrição
temporizadores setTimeout / setInterval Executa a função de retorno do temporizador quando ele expira, sujeito a um atraso mínimo de 1 ms
callbacks pendentes callbacks no nível do sistema callbacks de E/S adiados para o próximo ciclo (por exemplo, erros de TCP)
inativo, preparar Uso interno Uso interno pela libuv; os desenvolvedores geralmente não interagem com ele
poll callback de E/S Verifica se há novos eventos de E/S e executa os callbacks relacionados à E/S; se não houver temporizadores, o processo ficará bloqueado nesta etapa
check setImmediate A função de retorno setImmediate é executada nesta etapa
callbacks de fechamento eventos de fechamento Como, por exemplo, socket.on('close') — callbacks de fechamento


3. Microtarefas e macrotarefas

(1) (3.1) Fila de microtarefas

As microtarefas são executadas na íntegra após o término da fase atual e antes do início da próxima fase. A fila de microtarefas possui dois níveis de prioridade:

Microtarefa Prioridade Descrição
process.nextTick Máximo Priorizar a limpeza da fila nextTick após o término de cada fase
Promise.then / catch / finally Segundo maior Executado após a fila nextTick ser esvaziada

▶ Exemplo: nextTick tem precedência sobre Promise.then

JAVASCRIPT
process.nextTick(() => {
  console.log('nextTick 1');
});

Promise.resolve().then(() => {
  console.log('promise.then 1');
});

process.nextTick(() => {
  console.log('nextTick 2');
});

Promise.resolve().then(() => {
  console.log('promise.then 2');
});
▶ Experimente
TEXT 📖 Somente leitura
nextTick 1
nextTick 2
promise.then 1
promise.then 2

(2) (3.2) Fila de macrotarefas

As macrotarefas são agendadas por cada fase do ciclo de eventos, sendo que cada fase executa suas macrotarefas correspondentes.

Tarefa macro Fase Descrição
setTimeout / setInterval temporizadores Atraso mínimo de 1 ms; entra na fila de temporizadores ao expirar
setImmediate check Executado durante a fase “check” da próxima iteração
Callback de E/S poll Callback ao término de operações de E/S, como leitura/gravação de arquivos e operações de rede
callbacks de fechamento callbacks de fechamento Callbacks de evento de fechamento


4. setTimeout x setImmediate x process.nextTick

▶ Exemplo: A ordem de execução dos contextos externos é indefinida

JAVASCRIPT
setTimeout(() => {
  console.log('setTimeout');
}, 0);

setImmediate(() => {
  console.log('setImmediate');
});
▶ Experimente

Em um contexto que não envolva E/S, a ordem de execução de setTimeout e setImmediate é indefinida — ela depende do agendamento de processos do sistema, e qualquer uma delas pode ser executada primeiro.

▶ Exemplo: Em um contexto de E/S, setImmediate é sempre executado primeiro

JAVASCRIPT
const fs = require('fs');

fs.readFile(__filename, () => {
  setTimeout(() => {
    console.log('setTimeout');
  }, 0);

  setImmediate(() => {
    console.log('setImmediate');
  });
});
▶ Experimente
TEXT 📖 Somente leitura
setImmediate
setTimeout

Os callbacks de E/S são executados durante a fase de sondagem; assim que são concluídos, o processo passa para a fase de verificação (setImmediate) e só então avança para a próxima rodada da fase dos temporizadores (setTimeout).

▶ Exemplo: process.nextTick sempre vem primeiro

JAVASCRIPT
setTimeout(() => {
  console.log('setTimeout');
}, 0);

setImmediate(() => {
  console.log('setImmediate');
});

process.nextTick(() => {
  console.log('nextTick');
});
▶ Experimente
TEXT 📖 Somente leitura
nextTick
setTimeout   (or setImmediate, depends on scheduling)
setImmediate
Recurso setTimeout(fn, 0) setImmediate process.nextTick
Fila Tarefas macro (temporizadores) Tarefas macro (verificação) Microtarefas (prioridade máxima)
Fase de execução temporizadores verificação Imediatamente após o término da fase atual
Latência mínima 1 ms Nenhuma Nenhuma
Ordem do contexto de E/S Após setImmediate Antes de setTimeout Primeiro
Ordem do contexto externo Indefinido Indefinido Mais antigo


5. Comparação entre microtarefas e macrotarefas

Dimensão Microtarefa Macrotarefa
Representante process.nextTick, Promise.then setTimeout, setImmediate, callbacks de E/S
Momento de execução Após o término da fase atual e antes do início da próxima fase Durante a fase correspondente do ciclo de eventos
Estratégia de execução Limpar tudo de uma vez Executar uma por fase e, em seguida, verificar as microtarefas
Aninhadas As microtarefas geradas durante a execução também são eliminadas nesta rodada Adiadas para uma etapa posterior para execução
Prioridade Superior a todas as tarefas macro Inferior às microtarefas

▶ Exemplo: Microtarefas intercaladas entre macrotarefas

JAVASCRIPT
setTimeout(() => {
  console.log('timeout 1');
  Promise.resolve().then(() => {
    console.log('promise between 1 and 2');
  });
}, 0);

setTimeout(() => {
  console.log('timeout 2');
}, 0);
▶ Experimente
TEXT 📖 Somente leitura
timeout 1
promise between 1 and 2
timeout 2

Após a conclusão de cada macro-tarefa, a fila de micro-tarefas é esvaziada antes da execução da próxima macro-tarefa.



6. Os riscos de bloquear o ciclo de eventos e como evitá-los

▶ Exemplo: Bloqueando o ciclo de eventos

JAVASCRIPT
const http = require('http');

const server = http.createServer((req, res) => {
  const start = Date.now();
  while (Date.now() - start < 200) {}
  res.end('done');
});

server.listen(3000, () => {
  console.log('Server running on port 3000');
});
▶ Experimente

Cada solicitação bloqueia o ciclo de eventos por 200 ms; durante esse tempo, nenhuma outra solicitação pode ser processada.

▶ Exemplo: Dividindo tarefas de longa duração com setImmediate

JAVASCRIPT
function chunkedSort(arr, compareFn, callback) {
  const CHUNK = 1000;
  let offset = 0;

  function sortChunk() {
    const end = Math.min(offset + CHUNK, arr.length);
    for (let i = offset; i < end; i++) {
      for (let j = i + 1; j < end; j++) {
        if (compareFn(arr[i], arr[j]) > 0) {
          [arr[i], arr[j]] = [arr[j], arr[i]];
        }
      }
    }
    offset = end;
    if (offset < arr.length) {
      setImmediate(sortChunk);
    } else {
      callback(arr);
    }
  }

  setImmediate(sortChunk);
}

chunkedSort(
  [5, 3, 8, 1, 9, 2, 7, 4, 6],
  (a, b) => a - b,
  (result) => {
    console.log('Sorted:', result);
  }
);
▶ Experimente
Dimensão Código de bloqueio Código sem bloqueio
Método de cálculo Executar em um único ciclo síncrono Dividir em pequenos blocos e executar sequencialmente usando setImmediate
Ciclo de eventos Bloqueado; outras solicitações estão em espera O controle é cedido entre as fatias; outras chamadas de retorno podem ser executadas
Tempo de resposta Atraso acumulado Variação no atraso (aceitável para o usuário)
Utilização da CPU 100% em um único núcleo (picos) Utilização distribuída, reservando tempo para E/S
Casos de uso Cálculos muito curtos Cálculos longos, serviços de alta concorrência


7. Exemplo completo: script para verificar a ordem de execução do ciclo de eventos

O script a seguir verifica a ordem completa de execução de setTimeoutprocess.nextTicksetImmediatePromise.then → callback de E/S.

JAVASCRIPT
const fs = require('fs');

console.log('--- Script start ---');

setTimeout(() => {
  console.log('1. setTimeout (macrotask - timers)');
}, 0);

setImmediate(() => {
  console.log('2. setImmediate (macrotask - check)');
});

process.nextTick(() => {
  console.log('3. process.nextTick (microtask - highest)');
});

Promise.resolve().then(() => {
  console.log('4. Promise.then (microtask)');
});

fs.readFile(__filename, () => {
  console.log('5. I/O callback (poll phase)');

  setTimeout(() => {
    console.log('6.   inner setTimeout');
  }, 0);

  setImmediate(() => {
    console.log('7.   inner setImmediate');
  });

  process.nextTick(() => {
    console.log('8.   inner nextTick');
  });

  Promise.resolve().then(() => {
    console.log('9.   inner Promise.then');
  });
});

console.log('--- Script end ---');
TEXT 📖 Somente leitura
--- Script start ---
--- Script end ---
3. process.nextTick (microtask - highest)
4. Promise.then (microtask)
1. setTimeout (macrotask - timers)
2. setImmediate (macrotask - check)
5. I/O callback (poll phase)
8.   inner nextTick
9.   inner Promise.then
7.   inner setImmediate
6.   inner setTimeout

Análise da ordem de execução:

  1. O código síncrono é executado primeiro, exibindo “Início do script” e “Fim do script”
  2. Limpar as microtarefas após o término da fase atual: nextTick → Promise.then
  3. Entre na fase dos “temporizadores”: setTimeout
  4. Entrar na fase de verificação: setImmediate
  5. Executar a função de retorno de chamada durante a fase de sondagem, após a conclusão da E/S
  6. Limpe a microtarefa após a conclusão do callback de E/S: inner nextTick → inner Promise.then
  7. Quando a enquete terminar, insira o comando: inner setImmediate
  8. Próxima rodada de temporizadores: setTimeout interno

❓ Perguntas Frequentes

P: O setTimeout(fn, 0) realmente é executado após 0 ms? R: Não. Tanto os navegadores quanto o Node.js têm um atraso mínimo (aproximadamente 1 ms), e a execução precisa aguardar até que o ciclo de eventos entre na fase “timers”; o tempo de espera real depende da fase atual.

P: Qual é executado primeiro, process.nextTick ou Promise.then? R: process.nextTick é executado primeiro. nextTick tem a prioridade mais alta na fila de microtarefas, e a fila nextTick é sempre esvaziada antes da fila de Promises.

P: Por que setImmediate é executado antes de setTimeout em um callback de E/S? R: Os callbacks de E/S são executados durante a fase poll. Assim que essa fase termina, o processo passa diretamente para a fase check (setImmediate) antes de prosseguir para a próxima rodada da fase timers (setTimeout). Portanto, em um contexto de E/S, setImmediate sempre é executado antes de setTimeout.

P: O ciclo de eventos é de thread único ou multithread? R: A execução do JavaScript é de thread único, mas a libuv subjacente possui um pool de threads (4 threads por padrão) que lida com operações como consultas de DNS e E/S de arquivos. A E/S de rede é tratada pelo kernel do sistema operacional, que notifica o ciclo de eventos assim que a operação é concluída.

P: Como evitar bloquear o ciclo de eventos? R: Divida cálculos longos em partes menores e execute-os passo a passo usando setImmediate; utilize threads de trabalho para transferir tarefas que exigem muito da CPU para threads separadas; use E/S assíncrona em vez de E/S síncrona; e utilize fluxos para processar grandes conjuntos de dados em partes.

P: É possível chamar process.nextTick recursivamente de forma indefinida? R: Isso não é recomendado. O Node.js possui um limite padrão definido por process.maxTickDepth; chamadas recursivas a nextTick impedirão que o ciclo de eventos entre na próxima fase, levando à escassez de E/S. Você deve usar setImmediate em vez de chamadas recursivas de nextTick.


📖 Resumo


📝 Exercícios

  1. Conclua todos os exemplos de código desta lição e certifique-se de que cada um deles seja executado corretamente.
  2. Modifique o exemplo completo e adicione suas próprias extensões
  3. Analise a documentação oficial, identifique 1 ou 2 APIs que não foram abordadas nesta aula e escreva um código de teste para elas.
  4. Reflexão: Como você aplicaria o que aprendeu nesta aula a um projeto do mundo real?
  5. Tente combinar o que você aprendeu nesta aula com o conteúdo das aulas anteriores para criar um pequeno projeto.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%