JavaScript

Como Usar Iteração Avançada em JavaScript: Iterables, Generators Assíncronos em Produção

7 min de leitura

Como Usar Iteração Avançada em JavaScript: Iterables, Generators Assíncronos em Produção

Iterables e o Protocolo de Iteração Um iterable é um objeto que implementa o protocolo iterável do JavaScript, permitindo ser percorrido em loops como . Para ser iterable, um objeto deve ter um método que retorna um iterador — um objeto com um método que produz valores sequencialmente. Async generators são ideais para processar streams de dados, como ler linhas de um arquivo, consumir eventos de um servidor, ou paginar resultados de uma API. O aguarda automaticamente cada Promise, mantendo o código sincronizado e legível. Casos Práticos: Paginação e Streams Um exemplo real é iterar sobre resultados paginados sem conhecer o total de páginas antecipadamente. O async generator busca a próxima página conforme necessário: ${url}?page=${pagina} Esse padrão elimina a necessidade de fazer todas as requisições antecipadamente, economizando memória e permitindo cancelar a iteração a qualquer momento. Conclusão Iterables, generators e async generators formam uma progressão natural em JavaScript avançado. Começando com o protocolo iterável, você ganha controle fino sobre iterações

<h2>Iterables e o Protocolo de Iteração</h2>

<p>Um iterable é um objeto que implementa o protocolo iterável do JavaScript, permitindo ser percorrido em loops como <code>for...of</code>. Para ser iterable, um objeto deve ter um método <code>Symbol.iterator</code> que retorna um iterador — um objeto com um método <code>next()</code> que produz valores sequencialmente.</p>

<pre><code class="language-javascript">// Criando um iterable customizado

const intervalo = {

inicio: 1,

fim: 5,

[Symbol.iterator]() {

let atual = this.inicio;

return {

next: () =&gt; {

if (atual &lt;= this.fim) {

return { value: atual++, done: false };

}

return { done: true };

}

};

}

};

for (const num of intervalo) {

console.log(num); // 1, 2, 3, 4, 5

}</code></pre>

<p>Entender esse protocolo é fundamental porque ele é a base de todas as iterações avançadas em JavaScript. Arrays, Strings, Maps e Sets já implementam isso nativamente. Quando você cria um iterable customizado, ganha controle total sobre como os dados são consumidos, ideal para estruturas de dados complexas ou infinitas.</p>

<h3>Diferença entre Iterable e Iterator</h3>

<p>Um iterable é <em>quem pode ser iterado</em>, enquanto um iterator é <em>quem controla a iteração</em>. O iterator mantém estado interno (qual é o próximo valor) e possui o método <code>next()</code>. Um mesmo iterable pode ter múltiplos iteradores independentes operando simultaneamente.</p>

<pre><code class="language-javascript">const numeros = [1, 2, 3];

const iter1 = numeros[Symbol.iterator]();

const iter2 = numeros[Symbol.iterator]();

console.log(iter1.next()); // { value: 1, done: false }

console.log(iter2.next()); // { value: 1, done: false }

console.log(iter1.next()); // { value: 2, done: false }</code></pre>

<h2>Generators: Simplificando a Iteração</h2>

<p>Generators são funções especiais que retornam iterators de forma muito mais simples. Usando a sintaxe <code>function*</code> e a palavra-chave <code>yield</code>, você escreve código iterativo de forma linear, sem precisar gerenciar manualmente o método <code>next()</code> e o estado.</p>

<pre><code class="language-javascript">function* contador(max) {

for (let i = 1; i &lt;= max; i++) {

yield i;

}

}

const gen = contador(3);

console.log(gen.next()); // { value: 1, done: false }

console.log(gen.next()); // { value: 2, done: false }

console.log(gen.next()); // { value: 3, done: false }

console.log(gen.next()); // { value: undefined, done: true }

// Também funciona em for...of

for (const num of contador(3)) {

console.log(num); // 1, 2, 3

}</code></pre>

<p>O grande benefício é a legibilidade: você não precisa criar objetos com estruturas complexas. Generators mantêm o estado automaticamente, pausando na execução em cada <code>yield</code> e retomando de onde pararam. Isso é especialmente poderoso para processar grandes volumes de dados sem carregar tudo na memória.</p>

<h3>Generators com Comunicação Bidirecional</h3>

<p>Um aspecto avançado dos generators é a capacidade de receber valores através do método <code>next()</code>. Ao chamar <code>next(valor)</code>, esse valor é retornado da expressão <code>yield</code> anterior, permitindo comunicação bidirecional.</p>

<pre><code class="language-javascript">function* dialogoSimples() {

const resposta1 = yield &quot;Qual seu nome?&quot;;

const resposta2 = yield Olá ${resposta1}, quantos anos você tem?;

console.log(${resposta1} tem ${resposta2} anos);

}

const gen = dialogoSimples();

console.log(gen.next().value); // &quot;Qual seu nome?&quot;

console.log(gen.next(&quot;Maria&quot;).value); // &quot;Olá Maria, quantos anos você tem?&quot;

gen.next(25); // &quot;Maria tem 25 anos&quot;</code></pre>

<h2>Generators Assíncronos: Iteração com Async/Await</h2>

<p>Um async generator combina generators com <code>async/await</code>, permitindo iterar sobre dados assíncronos de forma elegante. A sintaxe é <code>async function*</code> e pode usar <code>yield</code> com Promises, <code>await</code> ou ambos.</p>

<pre><code class="language-javascript">// Async generator que simula buscar dados de uma API

async function* buscarDados(ids) {

for (const id of ids) {

const resposta = await fetch(https://api.exemplo.com/usuario/${id});

const dados = await resposta.json();

yield dados;

}

}

// Consumindo com for await...of

(async () =&gt; {

for await (const usuario of buscarDados([1, 2, 3])) {

console.log(usuario.nome);

}

})();</code></pre>

<p>Async generators são ideais para processar streams de dados, como ler linhas de um arquivo, consumir eventos de um servidor, ou paginar resultados de uma API. O <code>for await...of</code> aguarda automaticamente cada Promise, mantendo o código sincronizado e legível.</p>

<h3>Casos Práticos: Paginação e Streams</h3>

<p>Um exemplo real é iterar sobre resultados paginados sem conhecer o total de páginas antecipadamente. O async generator busca a próxima página conforme necessário:</p>

<pre><code class="language-javascript">async function* paginarResultados(url) {

let pagina = 1;

while (true) {

const resposta = await fetch(${url}?page=${pagina});

const dados = await resposta.json();

if (!dados.items || dados.items.length === 0) break;

for (const item of dados.items) {

yield item;

}

pagina++;

}

}

// Uso simples

(async () =&gt; {

for await (const item of paginarResultados(&#039;https://api.exemplo.com&#039;)) {

console.log(item);

}

})();</code></pre>

<p>Esse padrão elimina a necessidade de fazer todas as requisições antecipadamente, economizando memória e permitindo cancelar a iteração a qualquer momento.</p>

<h2>Conclusão</h2>

<p>Iterables, generators e async generators formam uma progressão natural em JavaScript avançado. Começando com o protocolo iterável, você ganha controle fino sobre iterações customizadas. Generators simplificam drasticamente essa lógica sem sacrificar funcionalidade. Async generators, por sua vez, trazem elegância para operações assíncronas complexas, substituindo callbacks e promises aninhadas por código linear e legível. Dominar esses três conceitos permite escrever código mais eficiente, escalável e profissional.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols" target="_blank" rel="noopener noreferrer">MDN - Iteration Protocols</a></li>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*" target="_blank" rel="noopener noreferrer">MDN - Generator Functions</a></li>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of" target="_blank" rel="noopener noreferrer">MDN - for await...of</a></li>

<li><a href="https://javascript.info/generators" target="_blank" rel="noopener noreferrer">JavaScript.info - Generators</a></li>

<li><a href="https://eloquentjavascript.net/11_async.html" target="_blank" rel="noopener noreferrer">Eloquent JavaScript - Chapter on Iterators</a></li>

</ul>

Comentários

Mais em JavaScript

Event Loop em JavaScript: Call Stack, Task Queue e Microtasks na Prática
Event Loop em JavaScript: Call Stack, Task Queue e Microtasks na Prática

Call Stack: O Coração da Execução O Call Stack é uma estrutura de dados que r...

Boas Práticas de ES Modules em JavaScript: import, export e Organização de Código para Times Ágeis
Boas Práticas de ES Modules em JavaScript: import, export e Organização de Código para Times Ágeis

O que são ES Modules? ES Modules (ECMAScript Modules) é o sistema oficial de...

Migração de JavaScript para TypeScript em Projetos Existentes: Do Básico ao Avançado
Migração de JavaScript para TypeScript em Projetos Existentes: Do Básico ao Avançado

Preparação e Diagnóstico do Projeto Antes de iniciar qualquer migração, você...