JavaScript: Loops, callbacks e Generators

Melhorando o uso de loops e callbacks com Generators!

Image for post
Image for post
Sem esse fluxo básico, não há software :D

Nesse artigo, iremos explorar duas maneiras de extrair funcionalidades ao criar loops no seu código: iremos usar iteração interna e iteração external.

1. O loop

Como exemplo, usaremos a função logFiles() a seguir:

const fs = require('fs');
const path = require('path');
function logFiles(dir) {
for (const fileName of fs.readdirSync(dir)) { // (A)
const filePath = path.resolve(dir, fileName);
console.log(filePath);
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
logFiles(filePath); // (B)
}
}
}
logFiles(process.argv[2]);

O loop iniciando na linha A registra os caminhos de arquivos da pasta atual. É uma combinação de um for-of-loop e uma recursão (a chamada recursiva está na linha B).

Vamos supor que por hora, você acha essa funcionalidade do loop útil (iterando sobre arquivos), mas não quer logar diretamente ao executar a função, como fazer?

2. Iteração interna

A primeira opção para extrair a funcionalidades de um loop, é através da iteração interna:

const fs = require('fs');
const path = require('path');
function logFiles(dir, callback) {
for (const fileName of fs.readdirSync(dir)) {
const filePath = path.resolve(dir, fileName);
callback(filePath); // (A)
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
logFiles(filePath, callback);
}
}
}
logFiles(process.argv[2], p => console.log(p));

Essa maneira de iteração é semelhante ao método Array.forEach(): logFiles() implementa o loop e invoca seu callback para cada valor da iteração (linha A).

3. Iteração externa

Uma alternativa à iteração interna é a iteração externa, onde faremos uso de Iterators ao criar uma função Generator:

const fs = require('fs');
const path = require('path');
function* logFiles(dir) {
for (const fileName of fs.readdirSync(dir)) {
const filePath = path.resolve(dir, fileName);
yield filePath;
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
yield* logFiles(filePath); // (A)
}
}
}
for (const p of logFiles(process.argv[2])) {
console.log(p);
}

Com iteração interna (primeiro exemplo) logFiles()chama a função callback ("push to"). Usando Generator, somos nós que chamamos a função ("pull from").

Observe que, usando Generators, você deve realizar as chamadas recursivas com yiled * (linha A). Se você apenas chamar logFiles(), ela retornará um objeto iterável.

Mas na verdade, queremos que yield retorne todos os valores desse objeto iterável. É por isso que utilizamos yield* .

Uma boa característica dos Generators é que é possível parar o processo de iteração interna. Sempre que logFiles()retornar um filePath, podemos examiná-lo imediatamente, realizar algo e depois, continuar logFiles().

É uma forma de criar funções multitarefas cooperativas, com yield pausando a tarefa atual, possibilitando a mudança de contexto para outra tarefa totalmente diferente.

4. Leitura adicional

⭐️ Créditos

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store