Back

Casos de Uso para Geradores em JavaScript

Casos de Uso para Geradores em JavaScript

Os geradores JavaScript (function*) fazem parte da linguagem desde o ES2015, mas muitos desenvolvedores frontend ainda recorrem a arrays ou cadeias de promises quando os geradores seriam uma opção mais adequada. O valor principal não está na velocidade bruta — está na avaliação preguiçosa (lazy evaluation), composabilidade e controle preciso sobre a iteração. Este artigo aborda situações onde os geradores realmente merecem seu lugar no código frontend moderno.

Pontos-Chave

  • Geradores produzem valores sob demanda através de avaliação preguiçosa, evitando computação desnecessária e arrays intermediários
  • A API Iterator Helpers traz métodos nativos map, filter e take para iteradores retornados por geradores, eliminando a necessidade de funções utilitárias personalizadas
  • yield* torna a travessia recursiva de árvores e grafos legível e preguiçosa
  • Geradores assíncronos (async function*) combinam com for await...of para lidar com busca de dados paginados ou em lotes com gerenciamento mínimo de estado

O Que Torna os Geradores JavaScript Diferentes

Uma função geradora retorna um iterador. Chamar a função não executa nenhum código — ela entrega um objeto com um método .next(). Cada chamada a .next() executa o corpo da função até o próximo yield, depois pausa, preservando o estado local entre as chamadas.

function* range(start, end) {
  for (let i = start; i < end; i++) yield i
}

for (const n of range(0, 5)) {
  console.log(n) // 0, 1, 2, 3, 4
}

Como os geradores implementam o protocolo de iterador, eles funcionam diretamente com for...of, sintaxe spread e desestruturação — nenhum adaptador necessário.

Iteração Preguiçosa em JavaScript: Processando Dados Sem Materializá-los

A razão principal para usar um gerador em vez de um array é a iteração preguiçosa: valores são produzidos apenas quando solicitados. Isso importa quando:

  • O conjunto de dados completo é grande e você precisa apenas de parte dele
  • Computar cada valor é custoso
  • A sequência é conceitualmente infinita
function* naturals() {
  let n = 0
  while (true) yield n++
}

// Computa valores apenas até o ponto de interrupção
for (const n of naturals()) {
  if (n > 100) break
}

Nenhum array intermediário é criado. Nenhum valor além do ponto de interrupção é computado.

A API Iterator Helpers: Pipelines Preguiçosos Nativos

Escrever utilitários personalizados de map, filter e take costumava ser boilerplate necessário. A API Iterator Helpers — agora disponível em todos os navegadores modernos — adiciona esses métodos diretamente aos iteradores síncronos:

const result = naturals()
  .filter(n => n % 2 === 0)
  .map(n => n * n)
  .take(5)
  .toArray() // [0, 4, 16, 36, 64]

Cada etapa é preguiçosa. .toArray() é o que dispara a avaliação. Isso torna pipelines baseados em geradores significativamente mais limpos sem bibliotecas de terceiros. Note que esses helpers se aplicam a iteradores síncronos — helpers de iteradores assíncronos ainda não estão padronizados em todos os ambientes.

Travessia de Árvores e Grafos

Geradores são uma escolha natural para percorrer estruturas recursivas. A travessia em profundidade (depth-first) de uma árvore semelhante ao DOM se torna direta:

function* walkTree(node) {
  yield node
  for (const child of node.children ?? []) {
    yield* walkTree(child)
  }
}

for (const node of walkTree(rootNode)) {
  if (node.type === 'input') processInput(node)
}

yield* delega para um gerador aninhado, mantendo a recursão legível e a travessia preguiçosa — você para assim que encontra o que precisa.

Geradores Assíncronos em JavaScript: Busca de Dados Paginados e em Lotes

async function* estende o padrão para sequências assíncronas. Combinado com for await...of, é adequado para respostas de API paginadas:

async function* fetchPages(url) {
  let nextUrl = url
  while (nextUrl) {
    const res = await fetch(nextUrl)
    const data = await res.json()
    yield data.items
    nextUrl = data.nextPage ?? null
  }
}

for await (const batch of fetchPages('/api/records')) {
  processBatch(batch)
}

Cada página é buscada apenas quando o loop avança. Não há necessidade de coletar todas as páginas antecipadamente ou gerenciar o estado de paginação externamente — o gerador o mantém.

Quando Não Usar Geradores

Geradores adicionam indireção. Para uma transformação simples de array que você consumirá inteiramente, métodos de array encadeados são mais claros. Use geradores quando a sequência for grande, potencialmente infinita, ou quando você precisar parar cedo sem computação desperdiçada.

Conclusão

Os geradores JavaScript brilham em três áreas: iteração preguiçosa sobre sequências grandes ou infinitas, pipelines de dados composáveis (especialmente com a API Iterator Helpers), e busca de dados assíncrona onde você precisa de controle sequencial e com estado. Eles não são uma substituição para arrays ou async/await — são a ferramenta certa quando você precisa produzir valores sob demanda em vez de todos de uma vez.

Perguntas Frequentes

Sim. Geradores funcionam bem para produzir sequências de dados que componentes React consomem. Você pode chamar um gerador dentro de um hook useEffect ou useMemo para computar valores preguiçosamente. No entanto, não use um gerador como a própria função do componente — React espera que componentes retornem JSX, não iteradores.

O gerador permanece pausado em seu último ponto de yield. Ele se torna elegível para coleta de lixo (garbage collection) assim que não houver mais referências ao seu objeto iterador. Se você precisar que a lógica de limpeza seja executada quando a iteração parar cedo, envolva o yield em um bloco try-finally. O bloco finally é executado quando o método return do iterador é chamado ou o gerador é coletado pelo garbage collector.

Para coleções pequenas totalmente consumidas, geradores têm uma leve sobrecarga do mecanismo de pausar e retomar. A diferença de desempenho é negligenciável na maioria das aplicações. Geradores se tornam mais rápidos na prática quando você processa grandes conjuntos de dados parcialmente, porque evitam alocar arrays intermediários e pulam a computação de valores que você nunca solicita.

Um gerador assíncrono produz valores incrementalmente à medida que ficam disponíveis, enquanto uma abordagem baseada em Promise espera até que todos os dados sejam coletados antes de retornar. Isso significa que geradores assíncronos permitem que você comece a processar o primeiro lote de resultados imediatamente, reduzem o uso de memória de pico e dão controle mais refinado sobre quando cada busca subsequente ocorre.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay