Programação Assíncrona em JavaScript
JavaScript é single-threaded — só consegue fazer uma coisa por vez. Mas se uma requisição de rede leva 3 segundos, a página deve congelar por 3 segundos? Claro que não. A programação assíncrona resolve isso: em vez de esperar, você pega uma senha e volta quando estiver pronto. Como um sistema de fila de restaurante — você não fica na porta da cozinha, senta e espera sua senha ser chamada.
Síncrono vs Assíncrono
Síncrono: O código executa linha por linha — a próxima linha espera a atual terminar. Como ficar na fila — você espera até que a pessoa à sua frente termine.
Assíncrono: Certas operações (requisições de rede, temporizadores, E/S de arquivo) iniciam sem bloquear o código subsequente. Quando terminam, você é notificado via callback ou Promise.
<script>
console.log('1');
setTimeout(function() {
console.log('2');
}, 1000);
console.log('3');
</script>
A ordem de saída é 1 → 3 → 2. setTimeout é assíncrono — seu callback executa após 1 segundo sem bloquear console.log('3').
Callbacks
Callbacks são o padrão assíncrono mais básico — passe uma função como argumento, e ela será chamada quando a operação assíncrona completar.
Exemplo: Uso Básico de Callback
<div id="output" style="padding: 10px; border: 1px solid #ccc;"></div>
<script>
const output = document.getElementById('output');
function buscarDados(callback) {
output.textContent = 'Carregando...';
setTimeout(function() {
callback('Dados carregados!');
}, 2000);
}
buscarDados(function(dados) {
output.textContent = dados;
});
</script>
Callback Hell
Quando operações assíncronas dependem umas das other em sequência, callbacks ficam cada vez mais aninhados — este é o infame "Callback Hell."
<script>
obterUsuario(function(usuario) {
obterPedidos(usuario.id, function(pedidos) {
obterDetalhePedido(pedidos[0].id, function(detalhe) {
obterItens(detalhe.itemId, function(itens) {
console.log('4 níveis de profundidade — adicione mais complexidade e fica ilegível');
});
});
});
});
</script>
Promise
Uma Promise é um objeto que representa o resultado eventual de uma operação assíncrona. Tem três estados:
| Estado | Descrição |
|---|---|
pending |
Em andamento, sem resultado ainda |
fulfilled |
Completado com sucesso, resultado disponível |
rejected |
Falhou, erro disponível |
Quando uma Promise transiciona de pending para fulfilled ou rejected, ela nunca volta atrás — como uma flecha uma vez disparada, não pode ser chamada de volta.
Criando uma Promise
<script>
const promise = new Promise(function(resolve, reject) {
// operação assíncrona
// chame resolve(resultado) em caso de sucesso
// chame reject(erro) em caso de falha
});
</script>
then / catch / finally
<script>
promise
.then(function(resultado) { console.log(resultado); }) // executa em caso de sucesso
.catch(function(erro) { console.error(erro); }) // executa em caso de falha
.finally(function() { console.log('Concluído'); }); // executa sempre
</script>
Exemplo: Simulando uma Requisição com Promise
<button id="loadBtn">Carregar Dados</button>
<div id="output" style="padding: 10px; border: 1px solid #ccc; margin-top: 10px;"></div>
<script>
const btn = document.getElementById('loadBtn');
const output = document.getElementById('output');
function buscarUsuario() {
return new Promise(function(resolve, reject) {
output.textContent = 'Carregando...';
setTimeout(function() {
const sucesso = true;
if (sucesso) {
resolve({ nome: 'Alice', idade: 25 });
} else {
reject(new Error('Falha no carregamento'));
}
}, 1500);
});
}
btn.addEventListener('click', function() {
buscarUsuario()
.then(function(usuario) {
output.textContent = 'Nome: ' + usuario.nome + ', Idade: ' + usuario.idade;
})
.catch(function(err) {
output.textContent = 'Erro: ' + err.message;
})
.finally(function() {
console.log('Requisição concluída (sucesso ou falha)');
});
});
</script>
Encadeamento
then em si retorna uma Promise, então você pode encadear chamadas — callback hell vira uma linha reta.
<script>
buscarUsuario()
.then(function(usuario) { return buscarPedidos(usuario.id); })
.then(function(pedidos) { return buscarDetalhe(pedidos[0].id); })
.then(function(detalhe) { console.log(detalhe); })
.catch(function(err) { console.error(err); });
</script>
catch no final do encadeamento captura erros de qualquer etapa — você não precisa de um catch após cada then.
async / await
async/await é açúcar sintático sobre Promisses — faz código assíncrono parecer síncrono, melhorando muito a legibilidade.
async: Declara uma função assíncrona que automaticamente retorna uma Promiseawait: Pausa a execução até a Promise ser resolvida, depois retorna o resultado
Exemplo: Reescrevendo a Requisição Acima com async/await
<button id="loadBtn">Carregar Dados</button>
<div id="output" style="padding: 10px; border: 1px solid #ccc; margin-top: 10px;"></div>
<script>
const btn = document.getElementById('loadBtn');
const output = document.getElementById('output');
function buscarUsuario() {
return new Promise(function(resolve) {
setTimeout(function() {
resolve({ nome: 'Bob', idade: 30 });
}, 1500);
});
}
btn.addEventListener('click', async function() {
try {
output.textContent = 'Carregando...';
const usuario = await buscarUsuario();
output.textContent = 'Nome: ' + usuario.nome + ', Idade: ' + usuario.idade;
} catch (err) {
output.textContent = 'Erro: ' + err.message;
} finally {
console.log('Requisição concluída');
}
});
</script>
async/await transforma "pegar uma senha e esperar" em "ficar na fila sem sair" — o fluxo do código é reto e a legibilidade vai lá em cima.
Tratamento de Erros
async/await usa try/catch para tratamento de erros — o mesmo padrão usado com código síncrono.
<script>
async function carregarTudo() {
try {
const usuario = await buscarUsuario();
const pedidos = await buscarPedidos(usuario.id);
console.log(pedidos);
} catch (err) {
console.error('Algo deu errado:', err);
}
}
</script>
Promise.all e Promise.race
| Método | Descrição |
|---|---|
Promise.all([p1, p2, p3]) |
Sucesso apenas se todas tiverem sucesso; falha assim que uma falhar |
Promise.race([p1, p2, p3]) |
Usa o resultado da primeira a resolver (sucesso ou falha) |
Exemplo: Requisições Concorrentes com Promise.all
<button id="loadAll">Carregar Concorrentemente</button>
<div id="output" style="padding: 10px; border: 1px solid #ccc; margin-top: 10px;"></div>
<script>
const btn = document.getElementById('loadAll');
const output = document.getElementById('output');
function atraso(ms, valor) {
return new Promise(function(resolve) {
setTimeout(function() { resolve(valor); }, ms);
});
}
btn.addEventListener('click', async function() {
output.textContent = 'Carregando...';
const resultados = await Promise.all([
atraso(1000, 'Dados do Usuário'),
atraso(1500, 'Dados de Pedidos'),
atraso(800, 'Dados de Configuração')
]);
output.textContent = 'Tudo carregado:\n' + resultados.join(' | ');
});
const raceBtn = document.createElement('button');
raceBtn.textContent = 'Carregamento Corrida';
document.body.appendChild(raceBtn);
raceBtn.addEventListener('click', async function() {
const maisRapido = await Promise.race([
atraso(1000, 'Fonte A (1s)'),
atraso(500, 'Fonte B (0.5s)'),
atraso(800, 'Fonte C (0.8s)')
]);
output.textContent = 'Mais rápido: ' + maisRapido;
});
</script>
Promise.all é como um jantar em grupo — todos devem chegar antes de comer. Promise.race é como uma corrida — quem terminar primeiro ganha. Use all para requisições independentes para acelerar, e race quando tiver múltiplas fontes espelho e quiser a mais rápida.
📖 Resumo
- Código síncrono bloqueia execução subsequente; código assíncrono não — single-threaded ainda consegue "fazer várias coisas"
- Callbacks são o padrão assíncrono mais básico; aninhamento profundo cria callback hell
- Promise tem três estados:
pending → fulfilledoupending → rejected— transições de estado são irreversíveis then/catch/finallyconsomem Promisses; encadeamento resolve callback hellasync/awaité açúcar sintático sobre Promises;awaitsó pode ser usado dentro de funçõesasyncPromise.allespera todas terem sucesso;Promise.racepega a primeira a resolver
❓ Perguntas Frequentes
P:
awaitpode ser usado em uma função comum? R: Não.awaitsó pode ser usado dentro de uma funçãoasync. Usarawaitem uma função comum causa erro de sintaxe.awaitde nível superior é suportado em módulos, mas o suporte do navegador varia.
P: O que acontece se uma das Promises em
Promise.allfalhar? R:Promise.allrejeita imediatamente com o primeiro erro. Outras Promises ainda executam, mas seus resultados são ignorados. Se precisar de "todas resolvidas independentemente do resultado," usePromise.allSettled.
P: O que uma função
asyncretorna? R: Uma funçãoasyncsempre retorna uma Promise. Mesmo que você façareturn 42, na verdade retornaPromise.resolve(42). Então você pode usar.then()para consumir o resultado.
📝 Atividades
- Básico: Use
Promise+setTimeoutpara simular um atraso de 2 segundos, depois exiba "Concluído" em caso de sucesso. - Intermediário: Reescreva o acima com
async/awaite adicione tratamento de erros comtry/catch. - Desafio: Use
Promise.allpara disparar 3 requisições simuladas com atrasos diferentes (1s/2s/3s) concorrentemente, depois calcule e exiba o tempo total decorrido quando todas completarem.



