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 afunction
palavra-chave. O asterisco indica quegenerate()
é um gerador, não uma função normal. - Segundo, a
yield
instruçã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 Generator
objeto sem executar seu corpo quando é invocado.
O Generator
objeto retorna outro objeto com duas propriedades: done
e value
. Em outras palavras, um Generator
objeto é iterável .
O seguinte chama o next()
método no Generator
objeto:
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 yield
instruçã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...of
loop:
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 forever
gerador, 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 Sequence
iterador 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 Bag
estrutura 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
yield
instruçã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...of
loop.