Array.fromAsync()

Como o método estático Array.fromAsync() transforma iteráveis assíncronos, generators assíncronos e resultados paginados de APIs em arrays, sem laços manuais com for await...of.

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

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.

caniuse

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().

Tecnologia:
js

Deixe seu Comentário!

Comentário
Nome
Email