Programação Assíncrona em JavaScript: guia completo na prática

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Programação Assíncrona em JavaScript: de callbacks a async/await



JavaScript é uma linguagem single-thread, mas seu modelo de execução permite processar eficientemente milhares de operações simultâneas sem bloquear a thread principal. Isso é possível graças à programação assíncrona. No Node.js, onde operações de entrada/saída (leitura de arquivos, consultas a banco de dados, chamadas de rede) ocupam 90% do tempo, entender a assincronicidade torna-se uma habilidade crítica. Neste guia, vamos abordar três estágios evolutivos do trabalho com assincronicidade: callbacks, promises e async/await, além de explorar os bastidores do Event Loop.



1. Callbacks: o fundamento da assincronicidade



Um callback é uma função que é passada para outra função como argumento e é chamada após a conclusão de uma operação assíncrona. Este é o padrão mais antigo e básico em JavaScript. No Node.js, muitos módulos nativos (fs, http) usam callbacks.



Exemplo de leitura de arquivo com callback:



const fs = require('fs');

console.log('Iniciando leitura do arquivo...');

fs.readFile('example.txt', 'utf8', (err, data) => { if (err) { console.error('Erro:', err); return; } console.log('Conteúdo do arquivo:', data);});

console.log('O código continua executando...');


A saída no console mostrará que "O código continua executando..." aparecerá antes do conteúdo do arquivo. Este é o comportamento não bloqueante: a thread principal não espera a leitura terminar, mas continua a execução.



Problema dos callbacks: Callback Hell

Com operações assíncronas aninhadas, o código se transforma em uma "pirâmide infernal":



fs.readFile('user.json', (err, userData) => {  if (err) throw err;  const user = JSON.parse(userData);  db.query(`SELECT * FROM posts WHERE user_id=${user.id}`, (err, posts) => {    if (err) throw err;    fs.writeFile('user_posts.json', JSON.stringify(posts), (err) => {      if (err) throw err;      console.log('Pronto!');    });  });});


Esse código é difícil de ler, depurar e manter. A solução são as promises.



2. Promises: encadeamento e tratamento de erros



Uma Promise é um objeto que representa o resultado futuro de uma operação assíncrona. Ela pode estar em um de três estados: pending (pendente), fulfilled (concluída com sucesso) ou rejected (rejeitada com erro).



Criação de uma promise simples:



const readFilePromise = (filename) => {  return new Promise((resolve, reject) => {    fs.readFile(filename, 'utf8', (err, data) => {      if (err) {        reject(err);      } else {        resolve(data);      }    });  });};

readFilePromise('example.txt') .then(data => console.log('Dados:', data)) .catch(err => console.error('Erro:', err));


Encadeamento de Promises (Chaining)

A principal vantagem é a capacidade de encadear operações sequenciais sem aninhamento:



readFilePromise('user.json')  .then(userData => {    const user = JSON.parse(userData);    return dbQueryPromise(`SELECT * FROM posts WHERE user_id=${user.id}`);  })  .then(posts => {    return writeFilePromise('user_posts.json', JSON.stringify(posts));  })  .then(() => console.log('Pronto!'))  .catch(err => console.error('Erro na cadeia:', err));


Execução paralela: Promise.all

Quando é necessário executar várias operações independentes simultaneamente:



const promise1 = readFilePromise('file1.txt');const promise2 = readFilePromise('file2.txt');const promise3 = readFilePromise('file3.txt');

Promise.all([promise1, promise2, promise3]) .then(results => { console.log('Todos os arquivos lidos:', results); }) .catch(err => console.error('Uma das promises falhou:', err));


Promise.all é concluída com sucesso apenas se todas as promises forem bem-sucedidas. Se pelo menos uma falhar, o catch será acionado.

Blogs

Book Recommendations