voltar aos artigosasync javascript
$cd~/artigos/callbacks-javascript-fundacao-assincrono
Async JavaScript
série 33 Conceitos JS·parte 21 de 38

Callbacks: A Função que te Liga de Volta

18 minVanderlei José Alamino

Por que o JavaScript não espera? Quando você configura um timer, faz uma requisição ou escuta um clique, como o código continua rodando em vez de congelar?

A resposta são callbacks: funções passadas para outras funções — "me liga de volta quando terminar". Conforme o MDN, um callback é passado como argumento e invocado dentro da função externa. Handlers de evento, timers e rede dependem deles.

snippetJavaScript
function processarEntrada(callback) {  callback("Alice");} processarEntrada(nome => console.log(`Olá, ${nome}!`));

Neste guia da série 33 Conceitos JS — projeto open source criado por Leonardo Maldonado — você vai aprender:

  • Callbacks síncronos vs assíncronos
  • Conexão com funções de alta ordem (map, filter, forEach)
  • Padrão error-first e inferno de callbacks
  • Por que Promises foram inventadas

Pré-requisitos: Event Loop (mecanismo que executa callbacks assíncronos) e familiaridade com funções de alta ordem — se você já usou map/filter, já conhece o padrão.

callbacks-javascript-fundacao-assincrono.meta.jsonjson

// metadados do artigo

{
  "tag": "async javascript",
  "readTime": "18 min",
  "author": "Vanderlei José Alamino",
  "source": "33jsconcepts.com",
  "series": "33 Conceitos JS",
  "part": {
    "current": 21,
    "total": 38
  }
}
abrir fonte original
callbacks-javascript-fundacao-assincrono.mdmarkdown

Fonte de referência

Conteúdo adaptado a partir do guia original em inglês.

33jsconcepts.com
01

seção

O que é um Callback?

Um callback é uma função passada como argumento para outra função, executada depois — "chamada de volta". Como um pager de restaurante: você não espera no balcão, dá uma forma de ser avisado e vai fazer outras coisas.

snippetJavaScript
function saudar(nome) {  console.log(`Olá, ${nome}!`);} function processarEntradaUsuario(callback) {  callback("Alice");} processarEntradaUsuario(saudar);  // "Olá, Alice!"

Nada de mágico — é função regular. O que a torna callback é como é usada: passada para ser executada depois. Pode ser nomeada, anônima ou arrow function:

snippetJavaScript
botao.addEventListener("click", handleClick);botao.addEventListener("click", () => console.log("Clicado!"));
02

seção

Analogia: Pager do Restaurante

snippettext
VOCÊ (código)              RESTAURANTE (runtime)"Quero hambúrguer"  ──►   COZINHA (Web APIs)                           setTimeout, fetch, click...Recebe pager          ◄──   BALCÃO (Fila de Callbacks)Vai sentar                    Event Loop chama quando pronto
snippetJavaScript
setTimeout(function comer() {  console.log("Comendo hambúrguer!");}, 5000); console.log("Sentando...");console.log("Conversando...");// Sentando → Conversando → (5s) Comendo hambúrguer!

O código não bloqueia — registra o callback e segue. É assim que a UI permanece responsiva.

03

seção

Callbacks e Funções de Alta Ordem

  • Função de alta ordem — aceita ou retorna funções
  • Callback — a função passada como argumento
snippetJavaScript
const numeros = [1, 2, 3]; numeros.forEach(num => console.log(num * 2));  // callback const adultos = usuarios.filter(u => u.idade >= 18);const nomes = usuarios.map(u => u.nome);

Se você usa map, filter, forEach, reduce, sort ou find, já entende callbacks. A diferença nos assíncronos é quando executam.

04

seção

Callbacks Síncronos vs Assíncronos

Síncronos — imediatos, bloqueantes

snippetJavaScript
console.log("Antes");const dobrados = [1, 2, 3].map(n => n * 2);console.log("Depois", dobrados);// Antes → map roda por completo → Depois

Exemplos: map, filter, forEach, reduce, sort, replace com função.

Assíncronos — depois, via event loop

snippetJavaScript
console.log("Antes");setTimeout(() => console.log("Timer"), 0);console.log("Depois");// Antes → Depois → Timer

Exemplos: setTimeout, addEventListener, fetch, fs.readFile (Node.js).

AspectoSíncronoAssíncrono
QuandoDurante a chamadaDepois, via event loop
Bloqueia?SimNão
Errostry/catch funcionatry/catch não captura!
snippetJavaScript
// try/catch FUNCIONA (síncrono)try {  [1, 2].forEach(n => { if (n === 2) throw new Error("2!"); });} catch (e) { console.log("Capturado"); } // try/catch NÃO FUNCIONA (assíncrono)try {  setTimeout(() => { throw new Error("Escapa!"); }, 100);} catch (e) { /* nunca roda */ }
05

seção

Callbacks e o Event Loop

snippetJavaScript
console.log("1: start");setTimeout(() => console.log("2: timeout"), 0);setTimeout(() => console.log("3: timeout"), 0);console.log("4: end");// 1 → 4 → 2 → 3
snippettext
1. Código síncrono roda (1, 4)2. Web APIs registram timers3. Call stack vazia → Event Loop pega callbacks da Task Queue4. Callbacks rodam na ordem (2, 3)

Para microtasks vs macrotasks e por que Promise.then() roda antes de setTimeout(..., 0), veja Event Loop.

06

seção

Padrões Comuns de Callback

Handlers de evento

snippetJavaScript
botao.addEventListener("click", evento => {  console.log("Clicado!", evento.target);});

Timers

snippetJavaScript
const id = setTimeout(() => console.log("Uma vez"), 2000);clearTimeout(id); let n = 0;const intervalId = setInterval(() => {  if (++n >= 5) clearInterval(intervalId);}, 1000);

Iteração de arrays (síncrono)

snippetJavaScript
produtos.forEach(p => console.log(p.nome));const nomes = produtos.map(p => p.nome);const disponiveis = produtos.filter(p => p.emEstoque);

Callbacks customizados

snippetJavaScript
function buscarUsuario(id, callback) {  setTimeout(() => {    callback({ id, nome: "Alice" });  }, 1000);} buscarUsuario(123, u => console.log(u.nome));console.log("Buscando...");
07

seção

Padrão Error-First (Node.js)

Convenção do Node.js: primeiro parâmetro sempre reservado para erro.

snippetJavaScript
function callback(erro, resultado) {  // erro: null se sucesso, Error se falha} fs.readFile("config.json", "utf8", (erro, dados) => {  if (erro) {    console.error(erro.message);    return;  // SEMPRE retorne após erro!  }  console.log(dados);});
snippetJavaScript
function dividirAsync(a, b, callback) {  setTimeout(() => {    if (b === 0) return callback(new Error("Divisão por zero"));    callback(null, a / b);  }, 100);}

Por que error-first? Consistência, erro visível primeiro, sem try/catch assíncrono.

08

seção

Inferno de Callbacks

Operações assíncronas encadeadas aninham callbacks — a pirâmide do destino:

snippetJavaScript
buscarUsuario(id, (err, user) => {  if (err) return tratarErro(err);  verificarSenha(user, pw, (err, ok) => {    if (err) return tratarErro(err);    buscarPerfil(user.id, (err, perfil) => {      if (err) return tratarErro(err);      buscarConfig(user.id, (err, cfg) => {        renderizarDashboard(user, perfil, cfg);      });    });  });});

Problemas: ilegível, erro duplicado em cada nível, difícil debugar e testar, variáveis de escopo confusas.

09

seção

Escapando do Inferno

1. Funções nomeadas — fluxo vertical, stack traces legíveis:

snippetJavaScript
function tratarUsuario(err, user) {  if (err) return tratarErro(err);  buscarPerfil(user.id, tratarPerfil);}buscarUsuario(id, tratarUsuario);

2. Retornos antecipados — caminho feliz no menor nível de indentação.

3. Modularização — autenticarUsuario, carregarPerfil em módulos separados.

4. Promises / async-await — solução moderna (próximo artigo da trilha):

snippetJavaScript
async function initDashboard(userId, senha) {  try {    const user = await buscarUsuario(userId);    const ok = await verificarSenha(user, senha);    if (!ok) throw new Error("Senha inválida");    const perfil = await buscarPerfil(user.id);    renderizarDashboard(user, perfil);  } catch (err) { tratarErro(err); }}

Promises e async/await são construídas sobre callbacks — .then(fn) ainda é callback por baixo.

10

seção

Erros Comuns

Chamar callback múltiplas vezes

snippetJavaScript
// ❌ finally chama callback de novofetch(url).then(r => cb(null, r)).catch(cb).finally(() => cb(null, "ok"));// ✅ uma chamada por caminhofetch(url).then(r => cb(null, r)).catch(cb);

Zalgo — misturar síncrono e assíncrono

snippetJavaScript
// ❌ cache hit = síncrono; miss = assíncronofunction buscar(cache, cb) {  if (cache.has("x")) cb(null, cache.get("x"));  // imediato!  else buscarServidor(cb);  // depois}// ✅ sempre assíncronoif (cache.has("x")) setTimeout(() => cb(null, cache.get("x")), 0);

Perder this em callbacks

snippetJavaScript
// ❌ function regular perde thissetTimeout(function() { console.log(this.nome); }, 100);// ✅ arrow, bind ou const self = thissetTimeout(() => console.log(this.nome), 100);

Veja this, call, apply & bind para detalhes.

Não tratar erros em callbacks assíncronos

Sempre verifique erro primeiro em callbacks error-first.

11

seção

Contexto Histórico

JavaScript (1995) foi single-threaded para simplicidade e segurança do DOM. Callbacks permitiram UI responsiva: registrar interesse, continuar, reagir depois.

AnoMarco
1995Callbacks de evento
1999XMLHttpRequest (AJAX)
2009Node.js — I/O com callbacks
2015ES6 Promises
2017async/await

Callbacks não estão obsoletos — eventos, arrays, Promises e async/await ainda dependem deles.

12

seção

Perguntas Clássicas de Entrevista

13

seção

Teste Seu Conhecimento

$ summary --quick

Resumo rápido

  • Callback = função passada para executar depois — pager de restaurante
  • Síncronos (map, filter) vs assíncronos (setTimeout, eventos) — quando executam
  • HOF e callback — mesma moeda: forEach(fn) passa fn como callback
  • Assíncronos usam event loop — rodam após código síncrono terminar
  • Error-first: callback(erro, resultado) — verifique erro primeiro, retorne cedo
  • try/catch não captura erros assíncronos — use error-first ou .catch()
  • Inferno de callbacks — aninhamento profundo = pirâmide ilegível
  • Escape: funções nomeadas, modularização, Promises/async-await
  • Promises ainda usam callbacks — .then() por baixo dos panos
  • Callbacks são a fundação — eventos, arrays, Promises, async/await

Créditos da série

Este artigo faz parte de uma série de estudos inspirada no projeto 33 Conceitos JS, criado por Leonardo Maldonado. Todo o conteúdo original está disponível em 33jsconcepts.com.