JavaScript Avançado

Guia Completo de Async Generators e Async Iterators em JavaScript na Prática

6 min de leitura

Guia Completo de Async Generators e Async Iterators em JavaScript na Prática

Entendendo Async Iterators Um async iterator é um objeto que implementa o protocolo assíncrono de iteração, permitindo percorrer dados que chegam de forma assíncrona. Diferentemente dos iteradores comuns, ele retorna uma Promise que resolve para . Para ser um async iterator, um objeto precisa implementar o método , que retorna o próprio iterator com um método que retorna uma Promise. Isso é fundamental quando trabalhamos com fluxos de dados, APIs ou leitura de arquivos que não estão imediatamente disponíveis. Async Generators na Prática Um async generator é uma função que combina e , permitindo pausar a execução com e retornar valores com . É a forma mais elegante e legível de criar async iterators. Generators assíncrono são especialmente úteis para consumir APIs, processar grandes volumes de dados ou implementar padrões de produção-consumo. A função retorna automaticamente um async iterator quando chamada. ${apiUrl}?page=${page}&limit=${pageSize} Exemplo com Leitura de Arquivo Padrões Avançados e Casos de Uso Composição e Transformação Você pode encadear múltiplos

<h2>Entendendo Async Iterators</h2>

<p>Um <strong>async iterator</strong> é um objeto que implementa o protocolo assíncrono de iteração, permitindo percorrer dados que chegam de forma assíncrona. Diferentemente dos iteradores comuns, ele retorna uma Promise que resolve para <code>{ value, done }</code>.</p>

<p>Para ser um async iterator, um objeto precisa implementar o método <code>[Symbol.asyncIterator]()</code>, que retorna o próprio iterator com um método <code>next()</code> que retorna uma Promise. Isso é fundamental quando trabalhamos com fluxos de dados, APIs ou leitura de arquivos que não estão imediatamente disponíveis.</p>

<pre><code class="language-javascript">// Exemplo: Iterator assíncrono simples

const asyncIterator = {

counter: 0,

[Symbol.asyncIterator]() {

return this;

},

async next() {

this.counter++;

if (this.counter &lt;= 3) {

return { value: Item ${this.counter}, done: false };

}

return { done: true };

}

};

// Usando for await...of

(async () =&gt; {

for await (const item of asyncIterator) {

console.log(item); // Item 1, Item 2, Item 3

}

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

<h2>Async Generators na Prática</h2>

<p>Um <strong>async generator</strong> é uma função que combina <code>async</code> e <code>function*</code>, permitindo pausar a execução com <code>await</code> e retornar valores com <code>yield</code>. É a forma mais elegante e legível de criar async iterators.</p>

<p>Generators assíncrono são especialmente úteis para consumir APIs, processar grandes volumes de dados ou implementar padrões de produção-consumo. A função <code>async function*</code> retorna automaticamente um async iterator quando chamada.</p>

<pre><code class="language-javascript">// Exemplo: Fetchando dados paginados

async function* fetchPaginatedData(apiUrl, pageSize) {

let page = 1;

let hasMore = true;

while (hasMore) {

const response = await fetch(${apiUrl}?page=${page}&amp;limit=${pageSize});

const data = await response.json();

if (data.items.length === 0) {

hasMore = false;

} else {

for (const item of data.items) {

yield item;

}

page++;

}

}

}

// Consumindo

(async () =&gt; {

for await (const item of fetchPaginatedData(&#039;https://api.example.com/users&#039;, 10)) {

console.log(item.name);

}

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

<h3>Exemplo com Leitura de Arquivo</h3>

<pre><code class="language-javascript">// Lendo arquivo linha por linha (Node.js)

const fs = require(&#039;fs&#039;);

const readline = require(&#039;readline&#039;);

async function* readFileLines(filePath) {

const fileStream = fs.createReadStream(filePath);

const rl = readline.createInterface({

input: fileStream,

crlfDelay: Infinity

});

for await (const line of rl) {

yield line;

}

}

// Usando

(async () =&gt; {

for await (const line of readFileLines(&#039;dados.txt&#039;)) {

console.log(line);

}

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

<h2>Padrões Avançados e Casos de Uso</h2>

<h3>Composição e Transformação</h3>

<p>Você pode encadear múltiplos async generators para criar pipelines de dados elegantes. Isso é particularmente poderoso para processamento ETL (Extract, Transform, Load).</p>

<pre><code class="language-javascript">// Filtrando dados

async function* filter(asyncIterable, predicate) {

for await (const item of asyncIterable) {

if (await predicate(item)) {

yield item;

}

}

}

// Transformando dados

async function* map(asyncIterable, transform) {

for await (const item of asyncIterable) {

yield await transform(item);

}

}

// Usando pipeline

(async () =&gt; {

const users = async function* () {

yield { id: 1, name: &#039;Alice&#039;, age: 28 };

yield { id: 2, name: &#039;Bob&#039;, age: 17 };

yield { id: 3, name: &#039;Charlie&#039;, age: 35 };

};

const adults = filter(users(), user =&gt; user.age &gt;= 18);

const names = map(adults, user =&gt; user.name.toUpperCase());

for await (const name of names) {

console.log(name); // ALICE, CHARLIE

}

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

<h3>Tratamento de Erros</h3>

<p>Erros em async generators devem ser tratados com try/catch tanto na função quanto durante a consumição, garantindo robustez.</p>

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

for (const url of urls) {

try {

const response = await fetch(url);

if (!response.ok) throw new Error(HTTP ${response.status});

yield await response.json();

} catch (error) {

console.error(Erro ao buscar ${url}:, error.message);

yield null; // ou use continue; para pular

}

}

}

(async () =&gt; {

for await (const data of safeDataFetch([&#039;url1&#039;, &#039;url2&#039;, &#039;url3&#039;])) {

if (data) console.log(data);

}

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

<h2>Performance e Boas Práticas</h2>

<p>Async generators são eficientes em memória porque processam um item por vez, ideal para grandes datasets. Nunca coloque toda a lógica assíncrona dentro de um único <code>await</code> no meio do generator — isso pode travar a iteração.</p>

<blockquote><p><strong>Dica importante:</strong> Use <code>for await...of</code> apenas quando realmente precisar de assincronicidade. Para arrays normais, continue usando <code>for</code> ou métodos como <code>map()</code> e <code>filter()</code>.</p></blockquote>

<pre><code class="language-javascript"></code></pre>

<h2>Conclusão</h2>

<p>Os principais aprendizados são: (1) <strong>Async iterators e generators</strong> oferecem uma forma elegante e eficiente de trabalhar com dados assíncrono em JavaScript, especialmente quando combinados com <code>for await...of</code>; (2) <strong>generators assíncrono</strong> (<code>async function*</code>) são a abordagem recomendada na maioria dos casos por sua legibilidade e capacidade de pausar/resumir com <code>await</code> e <code>yield</code>; (3) <strong>padrões de composição</strong> (filter, map, etc.) permitem criar pipelines robustos de processamento de dados sem carregar tudo na memória.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function*" target="_blank" rel="noopener noreferrer">MDN: Async Iterators and Generators</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://github.com/tc39/proposal-async-iteration" target="_blank" rel="noopener noreferrer">TC39 Proposal: Async Iteration</a></li>

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

<li><a href="https://eloquentjavascript.net/" target="_blank" rel="noopener noreferrer">Eloquent JavaScript - Cap. 11: Asynchronous Programming</a></li>

</ul>

Comentários

Mais em JavaScript Avançado

Como Usar Segurança em Node.js: Injeção, SSRF, Path Traversal e Hardening em Produção
Como Usar Segurança em Node.js: Injeção, SSRF, Path Traversal e Hardening em Produção

Injeção em Node.js A injeção é uma das vulnerabilidades mais críticas em apli...

Guia Completo de Promises Internamente: Implementando uma Promise do Zero
Guia Completo de Promises Internamente: Implementando uma Promise do Zero

O Que é uma Promise e Por Que Implementar do Zero Uma Promise é um objeto Jav...

Design Patterns em JavaScript: Strategy, Decorator e Composite: Do Básico ao Avançado
Design Patterns em JavaScript: Strategy, Decorator e Composite: Do Básico ao Avançado

Strategy Pattern: Flexibilidade no Comportamento O Strategy Pattern encapsula...