Complementando...
Vale lembrar que cada vez que vc chama a função geradora, um novo generator é criado:
function *generateId() {
let id = 0;
while (true) {
yield++id;
}
}
// cria um generator e extrai alguns valores
const gen = generateId();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
// cria outro generator (independente do primeiro)
const outroGen = generateId();
console.log(outroGen.next().value); // 1
console.log(outroGen.next().value); // 2
// um não interfere no outro
console.log(gen.next().value); // 4
console.log(outroGen.next().value); // 3
E como um Generator
é iterável, posso usá-lo em um for..of
:
// coloca um limite máximo para o id
function *generateId(maxId) {
let id = 0;
while (id < maxId) {
yield ++id;
}
}
for (const id of generateId(10))
console.log(id); // imprime os números de 1 a 10
Claro, se usar em um for
, tem que tomar cuidado quando o generator é infinito (neste caso, dentro do for
tem que controlar a condição de parada e usar break
, por exemplo).
Por fim, outra forma de pensar em um generator é imaginá-lo como uma função que pode ser "pausada". Cada vez que ela encontra um yield
, o valor é retornado, e ela fica pausada, esperando até que o próximo valor seja pedido (e só aí ela volta a executar, até encontrar o próximo yield
, ou até chegar ao final da função, que é quando ele se encerra).
Outro detalhe é que um generator só pode ser percorrido uma vez. Se precisar iterá-lo mais de uma vez, ou cria-se outro, ou guarda-se os valores em outra estutura (no exemplo acima, poderia fazer const valores = [...generateId(10)]
para guardar os valores em um array).