Código assíncrono: async/await
Baixar dados, esperar um timer: algumas coisas levam tempo, e o JavaScript não fica parado esperando. Entender isso muda como você lê código.
Estudar esta lição interativa →Até agora, todo código nosso rodou em linha reta: uma instrução termina, a próxima começa. Mas algumas tarefas levam tempo: baixar dados da internet, esperar 3 segundos, carregar uma imagem. E aqui vem a decisão de projeto mais importante do JavaScript: ele não fica parado esperando. Se ficasse, a página inteira congelaria durante a espera, sem rolagem, sem cliques, sem nada. Em vez disso, ele anota o que deve fazer quando a tarefa terminar e segue em frente com o resto do código. Esse comportamento tem nome: código assíncrono ("fora do compasso").
A linha do tempo do exemplo abaixo: o passo 1 roda, o timer é agendado em segundo plano, o passo 2 roda sem esperar, e só quando o timer termina o passo 3 acontece.
Veja isso ao vivo. O setTimeout agenda uma função para rodar dali a um tempo (em milissegundos). Rode o exemplo e repare que as mensagens não saem na ordem em que foram escritas: o "3" foi agendado, o "2" não esperou por ele:
// Muita coisa em JS acontece "depois" (assíncrono): esperar internet, timers...
console.log("1. Começou");
// setTimeout agenda algo para o futuro (aqui, 1 segundo):
setTimeout(() => {
console.log("3. Passou 1 segundo!");
}, 1000);
console.log("2. Essa linha não espera o timer!");
// Repare na ORDEM em que as mensagens aparecem no console.Rode e repare na ordem: 1, 2... e só depois o 3.
Esse "segue em frente" é ótimo para a página, mas complica a sua vida quando o passo seguinte depende do resultado da espera (não dá para exibir os dados antes de eles chegarem). Para essas horas, o JavaScript moderno tem uma dupla de palavras novas: async e await. Você marca a função com async (avisando: "esta função sabe esperar") e, dentro dela, escreve await antes da tarefa demorada. O await pausa aquela função (só ela, o resto da página continua vivo) até a tarefa terminar, e o código volta a ler de cima para baixo, como você sempre leu:
// async/await é o jeito moderno e legível de lidar com "espera".
function esperar(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function contagem() {
console.log("Preparar...");
await esperar(800); // pausa aqui até terminar
console.log("Já!");
await esperar(800);
console.log("Fim.");
}
contagem();
console.log("O resto do programa não espera: esta linha sai logo depois de Preparar...");É como pedir um café numa lanchonete movimentada. Você faz o pedido (dispara a tarefa) e o atendente não congela te encarando até o café sair: ele vai atendendo os outros (o programa segue). O await é o seu "me chama quando ficar pronto": quando o café sai, você continua exatamente de onde parou, com o café na mão.
O await só funciona dentro de uma função marcada com async. Escrever await numa função comum dá erro de sintaxe na hora. (A exceção é o topo de um arquivo em módulos, e este editor também aceita, mas dentro de funções a regra vale.) Se um dia isso acontecer com você, a correção costuma ser uma palavra: acrescente o async na frente da função. (Uma função async, aliás, sempre devolve uma Promise, a "promessa de um valor futuro" que é a engrenagem por trás disso tudo; você a verá com mais calma quando precisar dela.)