voltar aos artigosasync javascript
$cd~/artigos/generators-iterators-javascript-funcoes-pausaveis
Async JavaScript
série 33 Conceitos JS·parte 24 de 38

Generators & Iterators: Funções que Pausam e Sequências que Nunca Terminam

20 minVanderlei José Alamino

E se uma função pausasse no meio, retornasse um valor e retomasse de onde parou? E se valores fossem computados só quando você pede?

A resposta são generators (function*, yield) e iterators (protocolo .next() com { value, done }). Juntos habilitam lazy evaluation, sequências infinitas e pipelines de dados limpos.

snippetJavaScript
function* contagem(max) {  for (let i = 0; i < max; i++) yield i;} for (const n of contagem(3)) console.log(n);  // 0, 1, 2

Neste guia da série 33 Conceitos JS — projeto open source criado por Leonardo Maldonado — você vai aprender:

  • Protocolo de iteração e Symbol.iterator
  • yield vs return, yield* e bidirectional flow
  • Padrões práticos e async generators

Pré-requisitos: Closures, async/await e funções de alta ordem.

generators-iterators-javascript-funcoes-pausaveis.meta.jsonjson

// metadados do artigo

{
  "tag": "async javascript",
  "readTime": "20 min",
  "author": "Vanderlei José Alamino",
  "source": "33jsconcepts.com",
  "series": "33 Conceitos JS",
  "part": {
    "current": 24,
    "total": 38
  }
}
abrir fonte original
generators-iterators-javascript-funcoes-pausaveis.mdmarkdown

Fonte de referência

Conteúdo adaptado a partir do guia original em inglês.

33jsconcepts.com
01

seção

O que é um Iterator?

Um iterator define uma sequência e expõe .next() → { value, done }:

snippetJavaScript
function criarContador(max) {  let n = 0;  return {    next() {      return n < max        ? { value: n++, done: false }        : { value: undefined, done: true };    },  };} const it = criarContador(3);it.next();  // { value: 0, done: false }it.next();  // { value: 1, done: false }it.next();  // { value: 2, done: false }it.next();  // { value: undefined, done: true }

Por que não só array? Lazy evaluation (computa sob demanda) e memória estável — processe 1 milhão de registros um por vez.

Iterável built-inO que itera
Array, Stringelementos / caracteres
Map[key, value]
Setvalores únicos

for...of chama [Symbol.iterator]() e .next() até done: true.

02

seção

Analogia: Máquina de Doces

snippettext
Aperta botão (.next())  →  dispensa um doce (yield)Máquina PAUSA             →  espera próximo botãoLembra a posição          →  próximo doce, não o primeiroMáquina vazia             →  done: true

Generator = um valor por vez, pausando entre cada um.

03

seção

Generator Functions (function*)

snippetJavaScript
function* cores() {  console.log("Início");  yield "vermelho";  yield "verde";  return "fim";} const gen = cores();  // nada loga ainda!gen.next();  // loga "Início" → { value: "vermelho", done: false }gen.next();  // { value: "verde", done: false }gen.next();  // { value: "fim", done: true }

Generators são iterators — funcionam com for...of e spread:

snippetJavaScript
for (const c of cores()) console.log(c);  // vermelho, verde (sem "fim"!)console.log([...cores()]);  // ["vermelho", "verde"]
04

seção

yield em Profundidade

yieldreturn
PausaTermina
done: falsedone: true
Aparece em for...ofNão aparece em for...of

*yield** — delega para outro iterável:

snippetJavaScript
function* externo() {  yield 1;  yield* ["a", "b"];  yield 2;}console.log([...externo()]);  // [1, "a", "b", 2]

Valores para dentro — .next(valor) vira resultado do yield:

snippetJavaScript
function* chat() {  const nome = yield "Seu nome?";  yield `Olá, ${nome}!`;} const g = chat();g.next().value;        // "Seu nome?"g.next("Alice").value; // "Olá, Alice!"

Primeiro .next() inicia — valor passado é ignorado.

.return(v) termina cedo; .throw(e) injeta erro (capturável com try/catch no generator).

05

seção

Symbol.iterator

Objeto iterável tem [Symbol.iterator]() que retorna iterator:

snippetJavaScript
const colecao = {  itens: ["maçã", "banana"],  *[Symbol.iterator]() {    yield* this.itens;  },}; for (const item of colecao) console.log(item);

Classe Range:

snippetJavaScript
class Range {  constructor(inicio, fim, passo = 1) {    this.inicio = inicio; this.fim = fim; this.passo = passo;  }  *[Symbol.iterator]() {    for (let i = this.inicio; i <= this.fim; i += this.passo) yield i;  }} console.log([...new Range(1, 5)]);  // [1,2,3,4,5]
snippettext
for (const x of obj)  ≡  iterator = obj[Symbol.iterator]()                         while (!result.done) { x = result.value; ... }
06

seção

Lazy Evaluation e Sequências Infinitas

snippetJavaScript
function* range(inicio, fim) {  for (let i = inicio; i <= fim; i++) yield i;}// range(1, 1_000_000) — nada alocado até iterar

Sequência infinita:

snippetJavaScript
function* naturais() {  let n = 1;  while (true) yield n++;} const nums = naturais();nums.next().value;  // 1nums.next().value;  // 2

Fibonacci infinito e helper take(n, iterável):

snippetJavaScript
function* take(n, iter) {  let c = 0;  for (const x of iter) {    if (c++ >= n) return;    yield x;  }} // ❌ [...naturais()] — trava!// ✅ [...take(5, naturais())]
07

seção

Padrões Comuns

Gerador de ID: function* ids(p) { let i=1; while(true) yield ${p}_${i++}; }

Chunk/paginação: yield array.slice(i, i+size) em loop.

Pipeline lazy: generators filter, map compostos sobre range.

Máquina de estado: semáforo ciclando verde/amarelo/vermelho com yield.

Travessia de árvore: yield* recursivo em filhos.

08

seção

Async Generators e for await...of

Generator regular yield Promise pendente, não o valor:

snippetJavaScript
async function* buscarUsuarios() {  yield await fetch("/api/1").then(r => r.json());  yield await fetch("/api/2").then(r => r.json());} for await (const user of buscarUsuarios()) {  console.log(user.name);}

API paginada:

snippetJavaScript
async function* todasPaginas(url) {  let page = 1;  while (true) {    const data = await fetch(`${url}?page=${page}`).then(r => r.json());    if (!data.items.length) return;    yield data.items;    page++;  }}
AbordagemMelhor para
Promise.allPrecisa de todos antes de continuar
async generatorProcessar conforme chegam; parar cedo; memória
09

seção

Erros Comuns

Esquecer function*

yield em função regular → SyntaxError.

Esperar execução imediata

gen() só retorna objeto — chame .next() ou for...of.

return em vez de yield para iteração

Valor de return não entra em spread/for...of.

Reusar generator esgotado

Crie novo nums() a cada iteração.

Spread em generator infinito

Use take(n, gen) ou break manual.

Generator quando array basta

Lista fixa conhecida → use array.

10

seção

Perguntas Clássicas de Entrevista

11

seção

Teste Seu Conhecimento

$ summary --quick

Resumo rápido

  • Iterator — .next() retorna { value, done }
  • Generator — function* pausa em yield, retoma em .next()
  • yield pausa, return termina — return não entra em for...of
  • *yield** delega para outro iterável
  • Lazy — nada roda até você pedir o próximo valor
  • Sequências infinitas — possíveis com generators + take()
  • Symbol.iterator — torna objetos compatíveis com for...of
  • Async generators — async function* + for await...of
  • Generators são de uso único — esgotou, crie outro
  • Use quando lazy/infinito/async stream — array quando lista fixa

Créditos da série

Este artigo faz parte de uma série de estudos inspirada no projeto 33 Conceitos JS, criado por Leonardo Maldonado. Todo o conteúdo original está disponível em 33jsconcepts.com.