/** * Parte 8 — Iteração assíncrona. * * npx tsx src/09-iteracao-assincrona.ts */ import { buscarLivro, cronometro, type Livro } from './biblioteca.js'; // --------------------------------------------------------------------------- // 1. `for await...of` sobre um vetor de promises. // // Atenção: as chamadas já foram disparadas na criação do vetor, então elas // correm concorrentes. O laço apenas CONSOME em ordem. Isso é diferente de // um laço que chama a função a cada volta (arquivo 05). // --------------------------------------------------------------------------- async function consumirEmOrdem(): Promise { const marcar = cronometro(); const pendentes = [1, 2, 3].map((id) => buscarLivro(id)); for await (const livro of pendentes) { marcar(`for await : ${livro.titulo}`); } } // --------------------------------------------------------------------------- // 2. Gerador assíncrono: uma fonte que produz valores ao longo do tempo. // É o formato natural para paginação — o consumidor não sabe quantas // páginas existem, e não precisa saber. // --------------------------------------------------------------------------- /** Os identificadores de uma página do acervo — o "SELECT ... LIMIT/OFFSET". */ function idsDaPagina(pagina: number, tamanhoDaPagina: number): number[] { const inicio = (pagina - 1) * tamanhoDaPagina; return [1, 2, 3, 4].slice(inicio, inicio + tamanhoDaPagina); } async function* paginarAcervo(tamanhoDaPagina: number): AsyncGenerator { if (!Number.isInteger(tamanhoDaPagina) || tamanhoDaPagina < 1) { throw new RangeError('tamanhoDaPagina deve ser um inteiro positivo'); } let pagina = 1; while (true) { const ids = idsDaPagina(pagina, tamanhoDaPagina); if (ids.length === 0) return; yield Promise.all(ids.map((id) => buscarLivro(id))); pagina += 1; } } async function percorrerPaginas(): Promise { const marcar = cronometro(); for await (const paginaDeLivros of paginarAcervo(2)) { marcar(`página : ${paginaDeLivros.map((livro) => livro.id).join(', ')}`); } marcar('página : fim do acervo'); } // --------------------------------------------------------------------------- // 3. Limite de concorrência: nem tudo de uma vez, nem um de cada vez. // // `Promise.all` sobre 500 ids solicita 500 operações; pools podem limitar conexões. Processar // em lotes mantém o ganho da concorrência dentro de um teto conhecido. // --------------------------------------------------------------------------- async function emLotes( itens: readonly T[], tamanhoDoLote: number, tarefa: (item: T) => Promise, ): Promise { if (!Number.isInteger(tamanhoDoLote) || tamanhoDoLote < 1) { throw new RangeError('tamanhoDoLote deve ser um inteiro positivo'); } const resultados: R[] = []; for (let i = 0; i < itens.length; i += tamanhoDoLote) { const lote = itens.slice(i, i + tamanhoDoLote); resultados.push(...(await Promise.all(lote.map(tarefa)))); } return resultados; } async function comLimite(): Promise { const marcar = cronometro(); const titulos = await emLotes([1, 2, 3, 4], 2, async (id) => (await buscarLivro(id)).titulo); marcar(`lotes de 2: ${titulos.length} títulos em 2 rodadas`); } async function principal(): Promise { await consumirEmOrdem(); await percorrerPaginas(); await comLimite(); } void principal();