Funções Geradoras em JavaScript

Funções Geradoras em JavaScript

Neste artigo, explicaremos as funções geradoras em JavaScript.

YouTube Video

Funções Geradoras em JavaScript

As funções geradoras em JavaScript são funções especiais que diferem das funções regulares, pois podem realizar execução preguiçosa e pausar e retomar. Ao entender as funções geradoras, você pode lidar de maneira eficiente com operações assíncronas e o processamento sequencial de grandes volumes de dados. Aqui, forneceremos uma explicação detalhada sobre como as funções geradoras funcionam, como usá-las e exemplos práticos de uso.

O que são funções geradoras?

Uma função geradora é definida com function* (uma definição de função com um asterisco) e, ao contrário das funções regulares, pode pausar a execução no meio do caminho e retomá-la enquanto mantém seu estado. As funções geradoras permitem "execução preguiçosa" e retornam resultados incrementalmente, possibilitando um gerenciamento eficiente de memória e o processamento sequencial.

Sintaxe

1function* generatorFunction() {
2    yield 'First value';
3    yield 'Second value';
4    return 'Done';
5}

Dessa forma, uma função geradora pode ter várias expressões yield e usar yield para pausar sua execução. Quando uma função geradora é chamada, o corpo da função não é executado imediatamente, e um objeto gerador é retornado. Você pode chamar o método next() nesse objeto para retomar a execução da função a partir do ponto em que foi pausada.

Uso Básico de Funções Geradoras

A seguir, vejamos um exemplo básico de uso de funções geradoras.

 1function* simpleGenerator() {
 2    yield 1;
 3    yield 2;
 4    yield 3;
 5}
 6
 7const gen = simpleGenerator();
 8
 9console.log(gen.next()); // { value: 1, done: false }
10console.log(gen.next()); // { value: 2, done: false }
11console.log(gen.next()); // { value: 3, done: false }
12console.log(gen.next()); // { value: undefined, done: true }

Um ponto importante a observar aqui é que o gerador retorna valores a cada vez com yield, e enquanto a propriedade done for false, isso indica que há mais valores a serem gerados. A última chamada para next() retorna done: true, indicando que o gerador foi concluído.

A Palavra-chave yield e a Pausa de Valores

yield é uma palavra-chave que indica um ponto de pausa dentro de uma função geradora. O valor à direita de yield é retornado quando next() é chamado. Além disso, yield permite a comunicação bidirecional. Em outras palavras, ao passar um valor como argumento para o método next(), esse valor é enviado para a função geradora.

 1function* generatorWithYield() {
 2    const value1 = yield 'First yield';
 3    console.log('Received value:', value1);
 4    const value2 = yield 'Second yield';
 5    console.log('Received value:', value2);
 6}
 7
 8const gen = generatorWithYield();
 9
10console.log(gen.next());        // { value: 'First yield', done: false }
11console.log(gen.next('Apple')); // Received value: Apple
12                                // { value: 'Second yield', done: false }
13console.log(gen.next('Banana'));// Received value: Banana
14                                // { value: undefined, done: true }

Neste exemplo, a chamada next('Apple') envia o valor 'Apple' para a função geradora, onde ele é usado dentro da função.

Gerenciando o Estado do Gerador

Os geradores podem manter seu estado de execução, o que permite uma representação concisa de loops longos ou processamento sequencial. O exemplo a seguir demonstra um gerador que produz números indefinidamente.

 1function* infiniteGenerator() {
 2    let i = 0;
 3    while (true) {
 4        yield i++;
 5        if (i > 10) {
 6            break;
 7        }
 8    }
 9}
10
11const gen = infiniteGenerator();
12
13console.log(gen.next().value); // 0
14console.log(gen.next().value); // 1
15console.log(gen.next().value); // 2
16// Continues...

Este gerador produz continuamente números usando um loop while(true), permitindo que você obtenha valores conforme necessário. Isso possibilita o processamento eficiente de grandes conjuntos de dados.

Aplicações de Funções Geradoras

As funções geradoras são adequadas para executar múltiplos processos de forma sequencial. Por exemplo, elas são úteis para processar requisições de API de forma sequencial ou dividir e processar grandes arquivos.

1function* apiRequestGenerator() {
2    yield fetch('https://codesparklab.com/json/example1.json');
3    yield fetch('https://codesparklab.com/json/example2.json');
4    yield fetch('https://codesparklab.com/json/example3.json');
5}

Assim, os geradores, que podem ser usados para processamento assíncrono, são muito vantajosos para o manuseio eficiente de dados sequenciais.

Geradores Assíncronos

Geradores assíncronos introduzidos no ES2018 permitem retornar valores assíncronos sequencialmente, combinando async e yield. Isso torna possível escrever processamento assíncrono de forma concisa em combinação com await.

Sintaxe

 1async function* asyncGenerator() {
 2    yield await Promise.resolve(1);
 3    yield await Promise.resolve(2);
 4    yield await Promise.resolve(3);
 5}
 6
 7const gen = asyncGenerator();
 8
 9(async () => {
10    for await (const value of gen) {
11        console.log(value); // 1, 2, 3
12    }
13})();

Os geradores assíncronos podem obter valores sequencialmente usando um loop for await...of. Esse padrão é particularmente útil ao lidar com fluxos de dados assíncronos.

Exemplo Prático: Simplificando o Processamento Assíncrono com Geradores

As funções geradoras também são usadas para simplificar o fluxo do processamento assíncrono. Por exemplo, combinando yield e Promise conforme mostrado abaixo, você pode escrever operações assíncronas de forma que pareçam síncronas.

 1function* asyncFlow() {
 2    const data1 = yield fetch('https://codesparklab.com/json/example1.json');
 3    console.log(data1);
 4    const data2 = yield fetch('https://codesparklab.com/json/example2.json');
 5    console.log(data2);
 6}
 7
 8const gen = asyncFlow();
 9
10function handleAsync(generator) {
11    const next = (promise) => {
12        promise.then((result) => {
13        const { value, done } = generator.next(result);
14        if (!done) {
15            next(value);
16        }
17        });
18    };
19
20    next(generator.next().value);
21}
22
23handleAsync(gen);

Este código está fazendo requisições de API sequencialmente usando um gerador e processando os resultados.

Resumo

As funções geradoras são uma das características poderosas do JavaScript, possuindo a capacidade de pausar e retomar a execução de uma função. Isso permite o processamento sequencial, o processamento assíncrono e a manipulação eficiente de grandes conjuntos de dados. Compreender os geradores é um passo importante para dominar técnicas avançadas em JavaScript.

Você pode acompanhar o artigo acima usando o Visual Studio Code em nosso canal do YouTube. Por favor, confira também o canal do YouTube.

YouTube Video