Async/await e Array.map em TypeScript: 5 erros fatais

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

Async/await e Array.map: quando melhores amigos se tornam inimigos



Você já escreveu algo como const results = await array.map(async (item) => {...}) e obteve um array de Promise em vez dos dados esperados? Se sim — você não está sozinho. Esta pergunta no Stack Overflow acumulou dezenas de milhares de visualizações, e o número de desenvolvedores que pisaram neste rake é contado aos milhões.



Neste artigo, vamos analisar os 5 erros mais perigosos ao usar async/await com Array.map em TypeScript. E, mais importante, mostrarei como evitá-los. Aperte os cintos — vai doer, mas será útil.



Erro nº1: Esquecer o Promise.all (o mais popular)



O bug mais comum é pensar que await dentro de map magicamente esperará todas as promises. Spoiler: não, não espera.



Problema

// ❌ Assim não funcionaconst users = [1, 2, 3, 4, 5];const userData = await users.map(async (id) => {  const response = await fetch(`/api/users/${id}`);  return response.json();});// userData — é Promise[], não UserData[]!console.log(userData); // [Promise, Promise, Promise, Promise, Promise]


Por que isso acontece?

Array.map é síncrono. Ele não espera a conclusão dos callbacks assíncronos. Cada chamada da função async retorna uma Promise, e o map simplesmente as coleta em um array. O await antes de map espera apenas a conclusão do próprio map, mas não de seus callbacks.



Solução

// ✅ Correto: envolvemos com Promise.allconst users = [1, 2, 3, 4, 5];const userData = await Promise.all(  users.map(async (id) => {    const response = await fetch(`/api/users/${id}`);    return response.json();  }));// userData — UserData[]! 🎉console.log(userData);


Erro nº2: Todas as promises são executadas em paralelo (mas precisamos em sequência)



Promise.all executa todas as promises em paralelo. Isso é ótimo para desempenho, mas às vezes precisamos executar requisições estritamente uma após a outra. Por exemplo, ao trabalhar com uma API que limita o número de requisições, ou quando cada requisição seguinte depende da anterior.



Problema

// ❌ Todas as requisições vão simultaneamenteconst ids = [1, 2, 3, 4, 5];const results = await Promise.all(  ids.map(async (id) => {    // 5 requisições à API ao mesmo tempo — pode levar a rate limit    return await api.processItem(id);  }));


Solução: execução sequencial

// ✅ Usamos for...of com awaitconst ids = [1, 2, 3, 4, 5];const results: ItemData[] = [];

for (const id of ids) { const result = await api.processItem(id); results.push(result);}// results — array com dados processados em ordem


Alternativa — Array.reduce com acumulador de promise:

// ✅ Também funciona, mas menos legívelconst ids = [1, 2, 3, 4, 5];const results = await ids.reduce(async (accPromise, id) => {  const acc = await accPromise;  const result = await api.processItem(id);  return [...acc, result];}, Promise.resolve([] as ItemData[]));


Erro nº3: Tipagem incorreta no TypeScript



TypeScript pode não entender imediatamente o que você está fazendo com map assíncrono. Especialmente se você usar Promise.all sem especificar explicitamente o tipo.



Problema

// ❌ TypeScript reclama ou infere o tipo erradoconst ids = [1, 2, 3];const results = await Promise.all(  ids.map(async (id) => {    const data = await fetchData(id);    return data.name; // TypeScript pode pensar que é Promise<string>  }));// results tem tipo unknown ou Promise<string>[]


Solução: anotação explícita de tipo

// ✅ Especificamos explicitamente o tipo do resultado                

Blogs

Book Recommendations