JavaScript Assíncrono

JavaScript Assíncrono

O JavaScript executa uma coisa de cada vez, mas nunca trava enquanto espera. O event loop, callbacks, promises e async/await são a engrenagem que torna isso possível.

intermediate16 min readUpdated 15 de set. de 2026
js
// user.js
async function loadUser(id) {
  const res = await fetch(`/api/users/${id}`);
  if (!res.ok) throw new Error("User not found");
  return res.json();
}

const user = await loadUser(42);
console.log(user.name);
Modelo de execução
Event loop single-threaded
Primeira solução
Callbacks
Solução moderna
Promises + async/await
Estados da Promise
pending, fulfilled, rejected
Execução em paralelo
Promise.all / allSettled
Palavra-chave
async / await

Pratico

Observe o event loop

A ordem das mensagens mostra como o event loop agenda o trabalho.

Observe o event loop

A ordem das mensagens mostra como o event loop agenda o trabalho.

Por que importa

Por que o async é importante

Nunca bloqueia a página

Trabalhos lentos acontecem fora da thread principal enquanto a UI permanece responsiva, para que os usuários nunca fiquem encarando uma aba travada.

Lê-se como código síncrono

O async/await permite escrever lógica assíncrona de cima para baixo, sem aninhar callbacks ou encadear blocos then.

Feito para a rede

Cada fetch, timer, leitura de arquivo e chamada de banco de dados no JavaScript moderno é assíncrono, portanto, isso é fundamental, não opcional.

O panorama completo

As três peças do quebra-cabeça

O JavaScript assíncrono não é um único recurso, mas três camadas sobrepostas: o event loop, as promises e a sintaxe que parece código síncrono.

O event loop

Agendamento

Uma call stack, uma fila de tarefas (task queue) e uma fila de microtarefas (microtask queue) decidem a ordem em que seu código realmente executa.

Promises

Representação

Um objeto que representa um valor futuro, com uma maneira limpa de encadear tarefas e lidar com falhas.

async / await

Sintaxe

Uma camada fina sobre as promises que faz o código assíncrono parecer e ser lido como um código síncrono comum.

Async em resumo

As ferramentas do seu kit assíncrono

O event loop

Executa o trabalho enfileirado assim que o código síncrono atual termina.

Callbacks

O padrão original — passe uma função para ser executada quando o trabalho for concluído.

Promises

Objetos com três estados que permitem encadear manipuladores de sucesso e falha.

Combinadores

all, allSettled, race e any coordenam múltiplas promises simultaneamente.

async / await

Pausa uma função no await sem bloquear o restante do programa.

Tratamento de erros

catch e try/catch transformam promises rejeitadas em erros normais e tratáveis.

Uma breve historia

De callbacks ao async/await

  1. 2005

    AJAX populariza callbacks

    Gmail e Google Maps mostram ao mundo que as páginas podem ser atualizadas sem recarregar.

    05
  2. 2009

    Node.js adota o async

    O JavaScript no lado do servidor torna o I/O não bloqueante a maneira padrão de construir backends.

    09
  3. 2015

    Promises no ES6

    O objeto Promise é padronizado, dando ao código assíncrono um contrato compartilhado.

    15
  4. 2017

    async / await

    O ES2017 adiciona a sintaxe que faz o código assíncrono ser lido como código síncrono.

    17
  5. Hoje

    Async em todo lugar

    Fetch, streams, workers e a maioria das APIs modernas são baseadas em promises por padrão.

    Hoje

O guia completo

JavaScript Assíncrono: Tudo que voce precisa saber

Por que o JavaScript assíncrono existe

Computadores fazem muitas coisas de forma lenta: ler arquivos, consultar bancos de dados, esperar a resposta de um servidor. Se o JavaScript parasse e esperasse por cada uma dessas tarefas, a página travaria e os usuários iriam embora. A programação assíncrona é a maneira como a linguagem continua funcionando enquanto tarefas lentas acontecem em outro lugar.

O modelo mental é o de um restaurante. Um único garçom anota o seu pedido, o passa para a cozinha e imediatamente atende outra mesa. Quando a sua comida fica pronta, a cozinha toca um sino e o garçom a traz. O garçom nunca fica parado na cozinha observando a comida cozinhar. O JavaScript é esse garçom, e o event loop é o sino.

A call stack e o event loop

O JavaScript possui apenas uma call stack. Cada chamada de função adiciona um frame; quando ela retorna, o frame é removido. Como existe apenas uma stack, apenas uma coisa é executada por vez.

As APIs assíncronas são fornecidas pelo ambiente host, não pela linguagem. Quando você chama setTimeout, fetch ou a leitura de um arquivo, o navegador ou o Node.js gerencia a espera. Quando o trabalho termina, o callback correspondente é colocado em uma fila. O event loop verifica constantemente: a call stack está vazia? Se sim, ele pega o próximo callback da fila e o executa.

// order.js
console.log("start");

setTimeout(() => console.log("timeout"), 0);

Promise.resolve().then(() => console.log("promise"));

console.log("end");

// start, end, promise, timeout

Dois detalhes são importantes. Primeiro, o código síncrono sempre termina antes de qualquer callback ser executado. Segundo, existem duas filas: as microtasks (callbacks de promises) são executadas imediatamente após a tarefa atual, antes das tasks (timers, I/O). É por isso que promise é registrado no log antes de timeout.

Callbacks

A maneira original de lidar com resultados assíncronos é através de um callback — uma função que você passa para ser chamada posteriormente.

// callback.js
function getUser(id, callback) {
  setTimeout(() => {
    callback({ id, name: "Ada" });
  }, 300);
}

getUser(1, (user) => {
  console.log(user.name);
});

Callbacks funcionam, mas a composição deles é ruim. Quando cada etapa depende da anterior, você acaba aninhando funções, e o resultado é o callback hell: código que avança diagonalmente pela tela e torna o tratamento de erros doloroso.

// hell.js
getUser(1, (user) => {
  getPosts(user.id, (posts) => {
    getComments(posts[0].id, (comments) => {
      // three levels deep and counting
    });
  });
});

Promises

Uma Promise é um objeto que representa um valor que pode ainda não existir. Ela está em um de três estados:

  • pending — o trabalho ainda está em execução.
  • fulfilled — terminou com sucesso e retornou um valor.
  • rejected — falhou por algum motivo.

Uma vez liquidada (settled), a promise nunca muda de estado. Você anexa manipuladores (handlers) com then, catch e finally.

// promise.js
function getUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) resolve({ id, name: "Ada" });
      else reject(new Error("Invalid id"));
    }, 300);
  });
}

getUser(1)
  .then((user) => console.log(user.name))
  .catch((error) => console.error(error.message))
  .finally(() => console.log("done"));

O encadeamento (chaining) é a principal vantagem. Cada then retorna uma nova promise, portanto, em vez de aninhar, você pode linearizar a sequência e tratar todos os erros em um único catch ao final.

// chain.js
getUser(1)
  .then((user) => getPosts(user.id))
  .then((posts) => getComments(posts[0].id))
  .then((comments) => console.log(comments))
  .catch((error) => console.error("Something failed:", error));

Combinando promises

Quando várias operações assíncronas estão envolvidas, o construtor Promise fornece combinadores.

// combine.js
const [user, posts, settings] = await Promise.all([
  getUser(1),
  getPosts(1),
  getSettings(),
]);

const results = await Promise.allSettled([getUser(1), getUser(-1)]);

const fastest = await Promise.race([getFromCache(), getFromNetwork()]);

const firstSuccess = await Promise.any([mirrorA(), mirrorB()]);

O Promise.all executa tudo em paralelo e rejeita se qualquer promise for rejeitada. O Promise.allSettled aguarda todas e reporta cada resultado. O Promise.race finaliza com a primeira promise a ser resolvida ou rejeitada, e o Promise.any resolve com a primeira a ter sucesso. Escolher a opção correta altera drasticamente o comportamento de erro da sua aplicação.

async e await

async e await são sintaxes construídas sobre promises. Marcar uma função como async faz com que ela sempre retorne uma promise. Dentro dela, o await pausa a função até que uma promise seja resolvida, continuando então com o valor resolvido — sem bloquear o restante do programa.

// await.js
async function loadDashboard() {
  try {
    const user = await getUser(1);
    const posts = await getPosts(user.id);
    return { user, posts };
  } catch (error) {
    console.error("Failed to load dashboard", error);
    throw error;
  }
}

Isso parece um código síncrono, que é exatamente o objetivo. Erros são tratados com try/catch comuns, e o finally continua funcionando. A função retorna uma promise, então quem a chama pode usar await ou encadear .then como de costume.

Sequencial vs paralelo

Um erro sutil, mas caro, é aguardar a conclusão de tarefas independentes uma por uma. Cada await espera pela anterior, fazendo com que o tempo total seja a soma de todas. Quando as tarefas não dependem umas das outras, inicie-as simultaneamente.

// parallel.js
// Sequential — slow, ~600ms
const a = await slowTask("a");
const b = await slowTask("b");

// Parallel — fast, ~300ms
const [x, y] = await Promise.all([slowTask("x"), slowTask("y")]);

Use awaits sequenciais apenas quando uma etapa posterior realmente precise de um resultado anterior, como buscar um usuário antes de buscar as postagens desse usuário.

Tratamento de erros em código async

Promises rejeitadas que ninguém trata tornam-se unhandled rejections, o que pode derrubar processos Node.js e lotar o console nos browsers.

// errors.js
try {
  const res = await fetch("/api/data");
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data = await res.json();
} catch (error) {
  showMessage("Could not load data. Please try again.");
} finally {
  hideSpinner();
}

Prefira try/catch em volta de chamadas com await, lance objetos Error (não strings) e sempre trate ou relance o erro deliberadamente. O guia de Tratamento de Erros aprofunda-se em estratégias e mensagens voltadas para o usuário.

Armadilhas comuns

  • Esquecer o await. A função retorna uma promise, não o valor, então a linha seguinte é executada cedo demais.
  • forEach com callbacks assíncronos. O forEach ignora promises retornadas; use for...of ou Promise.all com map.
  • Usar await em um loop quando a execução paralela seria melhor. Funciona, mas é desnecessariamente lento.
  • Não retornar dentro de then. A ausência de um return quebra a cadeia e faz com que o valor seja perdido.
  • Presumir a ordem de execução. Apenas await e a ordenação de microtasks garantem a sequência; tarefas concorrentes terminam quando terminarem.
  • Ignorar erros com um catch vazio, escondendo o bug que você precisava encontrar.

Melhores práticas

  • Prefira async/await em vez de longas cadeias de .then para melhorar a legibilidade.
  • Execute tarefas independentes em paralelo com Promise.all ou Promise.allSettled.
  • Sempre trate rejeições, mesmo que seja apenas para registrar o log e relançar o erro.
  • Mantenha as funções async pequenas e atribua a elas apenas uma responsabilidade.
  • Use Promise.allSettled quando resultados parciais forem aceitáveis.
  • Cancele tarefas que não são mais necessárias com um AbortController.
  • Exiba estados de carregamento e de erro para que os usuários entendam o que está acontecendo.

Próximos passos

Agora você tem o toolkit assíncrono completo. Coloque-o em prática com a Fetch API, onde promises e await impulsionam cada requisição de rede, e refine-o com os padrões de Tratamento de Erros.

Encadeamento vs awaiting

async/await remove callbacks aninhados e torna a sequência óbvia.

Preferir
async function load() {
  const user = await getUser(1);
  const posts = await getPosts(user.id);
  return posts;
}
Evitar
function load() {
  return getUser(1).then((user) => {
    return getPosts(user.id).then((posts) => {
      return posts;
    });
  });
}

Trabalho independente

Se as requisições não dependem umas das outras, inicie-as juntas com Promise.all.

Preferir
const [user, posts] = await Promise.all([
  getUser(1),
  getPosts(1),
]);
Evitar
const user = await getUser(1);
const posts = await getPosts(1);
// runs one after the other

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender Async JavaScript?

Nosso tutorial interativo te guia por Async JavaScript passo a passo — com quizzes e codigo real que voce pode executar no navegador.