Transformar um iterável assíncrono (como um ReadableStream, um generator assíncrono ou o resultado paginado de uma API) em um array sempre exigiu um laço manual com for await...of e um array acumulador. O método estático Array.fromAsync() resolve isso de forma nativa, devolvendo uma Promise que resolve para o array completo.
O jeito antigo
async function paraArray(iteravelAssincrono) {
const resultado = [];
for await (const item of iteravelAssincrono) {
resultado.push(item);
}
return resultado;
}
const itens = await paraArray(gerarItens());
Funciona, mas é um utilitário que praticamente todo projeto acaba reescrevendo (ou importando de uma biblioteca) na primeira vez que precisa consumir um async iterable inteiro.
Como funciona o Array.fromAsync()
A assinatura é a mesma do já conhecido Array.from(), mas o retorno é uma Promise que resolve para o array final:
async function* gerarItens() {
yield 1;
yield 2;
yield 3;
}
const itens = await Array.fromAsync(gerarItens());
console.log(itens); // [1, 2, 3]
Também funciona com iteráveis síncronos e Promises
Assim como Array.from(), o método aceita uma função de mapeamento como segundo argumento — e ela pode até retornar Promises, que são aguardadas automaticamente:
const ids = [1, 2, 3];
const usuarios = await Array.fromAsync(ids, id =>
fetch(`/api/usuarios/${id}`).then(res => res.json())
);
console.log(usuarios); // array com os 3 usuários já resolvidos
Sem o fromAsync(), esse mesmo código exigiria um Promise.all() envolvendo um map(), ou um laço assíncrono manual.
Consumindo respostas paginadas
Um caso de uso comum é reunir todas as páginas de uma API em um único array, usando um async generator para buscar cada página sob demanda:
async function* buscarPaginas(url) {
let proxima = url;
while (proxima) {
const res = await fetch(proxima);
const pagina = await res.json();
yield* pagina.itens;
proxima = pagina.proximaPagina;
}
}
const todosOsItens = await Array.fromAsync(buscarPaginas('/api/produtos'));
Diferença importante em relação ao Array.from()
Usar Array.from() (sem o fromAsync) em um async generator não gera o erro esperado — ele apenas cria um array de Promises não resolvidas, porque Array.from() não sabe que precisa dar await em cada item:
async function* numeros() {
yield 1;
yield 2;
}
Array.from(numeros()); // TypeError: numeros(...) is not iterable
// generators assíncronos só implementam Symbol.asyncIterator,
// e Array.from() só entende Symbol.iterator
Array.fromAsync() reconhece tanto Symbol.iterator quanto Symbol.asyncIterator, o que o torna a escolha correta sempre que houver qualquer chance de o iterável ser assíncrono.
Quando usar
- Consumir um async generator ou um
ReadableStreaminteiro de uma vez. - Reunir resultados paginados de uma API em um único array.
- Aplicar uma função de mapeamento assíncrona (como um
fetchpor item) sem escreverPromise.all()manualmente. - Substituir o padrão repetitivo do laço
for await...ofcom array acumulador.
Compatibilidade
O suporte já é bom nos navegadores modernos e no Node.js recente, mas vale sempre conferir antes de usar em produção, principalmente se o projeto precisar suportar ambientes mais antigos.
Conclusão
Array.fromAsync() preenche uma lacuna que existia desde a chegada dos async generators, oferecendo uma forma nativa e direta de materializar um iterável assíncrono em array, sem laços manuais nem gambiarras com Promise.all().
0 Comentário
Deixe seu Comentário!