Geradores JavaScript

Resumo : neste tutorial, você aprenderá sobre geradores JavaScript e como usá-los de forma eficaz.

Introdução aos geradores JavaScript

Em JavaScript, uma função regular é executada com base no modelo de execução até a conclusão. Ele não pode fazer uma pausa no meio do caminho e continuar de onde parou. Por exemplo:

function foo() {
    console.log('I');
    console.log('cannot');
    console.log('pause');
}Linguagem de código:  JavaScript  ( javascript )

A foo()função é executada de cima para baixo. A única maneira de sair foo()é retornando dele ou gerando um erro. Se você invocar a foo()função novamente, ela iniciará a execução de cima para baixo.

foo();

Saída:

I
cannot
pause

ES6 introduz um novo tipo de função diferente de uma função normal: gerador de função ou gerador.

Um gerador pode fazer uma pausa no meio do caminho e depois continuar de onde parou. Por exemplo:

function* generate() {
    console.log('invoked 1st time');
    yield 1;
    console.log('invoked 2nd time');
    yield 2;
}Linguagem de código:  JavaScript  ( javascript )

Vamos examinar a generate()função em detalhes.

  • Primeiro, você vê o asterisco ( *) após a functionpalavra-chave. O asterisco indica que generate()é um gerador, não uma função normal.
  • Segundo, a yieldinstrução retorna um valor e pausa a execução da função.

O código a seguir invoca o generate()gerador:

let gen = generate();Linguagem de código:  JavaScript  ( javascript )

Quando você invoca o generate()gerador:

  • Primeiro, você não vê nada no console. Se generate()fosse uma função regular, você esperaria ver algumas mensagens.
  • Segundo, você recebe algo generate()como valor retornado.

Vamos mostrar o valor retornado no console:

console.log(gen);Linguagem de código:  JavaScript  ( javascript )

Saída:

Object [Generator] {}Linguagem de código:  CSS  ( css )

Portanto, um gerador retorna um Generatorobjeto sem executar seu corpo quando é invocado.

O Generatorobjeto retorna outro objeto com duas propriedades: donee value. Em outras palavras, um Generatorobjeto é iterável .

O seguinte chama o next()método no Generatorobjeto:

let result = gen.next();
console.log(result);Linguagem de código:  JavaScript  ( javascript )

Saída:

invoked 1st time
{ value: 1, done: false }
Linguagem de código:  CSS  ( css )

Como você pode ver, o objeto Generator executa seu corpo que gera uma mensagem 'invoked 1st time'na linha 1 e retorna o valor 1 na linha 2.

A yieldinstrução retorna 1 e pausa o gerador na linha 2.

Da mesma forma, o código a seguir invoca o next()método do Gerador pela segunda vez:

result = gen.next();
console.log(result);
Linguagem de código:  JavaScript  ( javascript )

Saída:

invoked 2nd time
{ value: 2, done: false }Linguagem de código:  CSS  ( css )

Desta vez o Gerador retoma sua execução a partir da linha 3 que gera a mensagem 'invoked 2nd time'e retorna (ou produz) 2.

O seguinte invoca o next()método do objeto gerador uma terceira vez:

result = gen.next();
console.log(result);Linguagem de código:  JavaScript  ( javascript )

Saída:

{ value: undefined, done: true }Linguagem de código:  CSS  ( css )

Como um gerador é iterável, você pode usar o for...ofloop:

for (const g of gen) {
    console.log(g);
}Linguagem de código:  JavaScript  ( javascript )

Aqui está a saída:

invoked 1st time
1
invoked 2nd time
2

Mais exemplos de geradores de JavaScript

O exemplo a seguir ilustra como usar um gerador para gerar uma sequência sem fim:

function* forever() {
    let index = 0;
    while (true) {
        yield index++;
    }
}

let f = forever();
console.log(f.next()); // 0
console.log(f.next()); // 1
console.log(f.next()); // 2
Linguagem de código:  JavaScript  ( javascript )

Cada vez que você chama o next()método do forevergerador, ele retorna o próximo número na sequência começando em 0.

Usando geradores para implementar iteradores

Ao implementar um iterador, você deve definir manualmente o next()método. No next()método, você também deve salvar manualmente o estado do elemento atual.

Como os geradores são iteráveis, eles podem ajudá-lo a simplificar o código para implementar o iterador.

A seguir está um Sequenceiterador criado no tutorial do iterador :

class Sequence {
    constructor( start = 0, end = Infinity, interval = 1 ) {
        this.start = start;
        this.end = end;
        this.interval = interval;
    }
    [Symbol.iterator]() {
        let counter = 0;
        let nextIndex = this.start;
        return  {
            next: () => {
                if ( nextIndex < this.end ) {
                    let result = { value: nextIndex,  done: false }
                    nextIndex += this.interval;
                    counter++;
                    return result;
                }
                return { value: counter, done: true };
            }
        }
    }
}
Linguagem de código:  JavaScript  ( javascript )

E aqui está o novo iterador Sequence que usa um gerador:

class Sequence {
    constructor( start = 0, end = Infinity, interval = 1 ) {
        this.start = start;
        this.end = end;
        this.interval = interval;
    }
    * [Symbol.iterator]() {
        for( let index = this.start; index <= this.end; index += this.interval ) {
            yield index;
        }
    }
}Linguagem de código:  JavaScript  ( javascript )

Como você pode ver, o método Symbol.iterator é muito mais simples usando o gerador.

O script a seguir usa o iterador Sequence para gerar uma sequência de números ímpares de 1 a 10:

let oddNumbers = new Sequence(1, 10, 2);

for (const num of oddNumbers) {
    console.log(num);
}Linguagem de código:  JavaScript  ( javascript )

Saída:

1
3
5
7
9

Usando um gerador para implementar a estrutura de dados Bag

Um Bag é uma estrutura de dados que tem a capacidade de coletar elementos e iterar por meio de elementos. Não oferece suporte à remoção de itens.

O script a seguir implementa a Bagestrutura de dados:

class Bag {
    constructor() {
        this.elements = [];
    }
    isEmpty() {
        return this.elements.length === 0;
    }
    add(element) {
        this.elements.push(element);
    }
    * [Symbol.iterator]() {
        for (let element of this.elements) {
            yield element;
        }
    }
}

let bag = new Bag();

bag.add(1);
bag.add(2);
bag.add(3);

for (let e of bag) {
    console.log(e);
}Linguagem de código:  JavaScript  ( javascript )

Saída:

1
2
3

Resumo

  • Geradores são criados pela função gerador function* f(){}.
  • Os geradores não executam seu corpo imediatamente quando são invocados.
  • Os geradores podem fazer uma pausa no meio do caminho e retomar suas execuções onde foram pausadas. A yieldinstrução pausa a execução de um gerador e retorna um valor.
  • Os geradores são iteráveis ​​para que você possa usá-los com o for...ofloop.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *