Usando um Generator Function para definir um interador personalizado
As generator functions aprimoram o processo de definição do protocolo iterável fornecendo um algoritmo iterativo. Quando chamada, uma generator function não se executa seu corpo imediatamente. Em vez disso, ele retorna um tipo especial de iterador conhecido como generator object, conforme mostrado na imagem a seguir.
Podemos executar o corpo da generator function chamando seu método next(). O yield palavra-chave que pausa o generator e especifica o valor a ser retornado. Vamos demostrar em um algoritmo fácil de implementar. Observe o asterisco após a palavra-chave function na linha 5. Este é o nosso generator function e define um iterador personalizado para coleção:
const collection = {
a: 10,
b: 20,
c: 30,
[Symbol.iterator]: function* () { // ⇒ linha 5
for (let key in this) {
yield this[key];
}
}
}
const iterator = collection[Symbol.iterator]();
console.log(iterator.next()); // ⇒ {value: 10, done: false}
console.log(iterator.next()); // ⇒ {value: 20, done: false}
console.log(iterator.next()); // ⇒ {value: 30, done: false}
console.log(iterator.next()); // ⇒ {value: undefined, done: true}
Usamos um loop for...in dentro do generator para iterar sobre as propriedades do objeto. A cada iteração, a palavra-chave yield interrompe a execução do loop e retorna o valor da propriedade seguinte ao caller. É possível chamar uma generator function quantas vezes forem necessárias, e cada vez que é chamado ele retorna um novo generator object. Mas um generator object pode ser iterado apenas uma vez. Como o objeto retornado por um generator é sempre um iterador, pode usar a sintaxe for...of para iterar sobre o resultado também.
for (let value of collection) {
console.log(value);
}
// return
// 10
// 20
// 30
const collection2 = {
a: 10,
b: 20,
c: 30
}
for (let value of collection2) {
console.log(value);
}
// return
// TypeError: collection2 is not iterable
Agora que sabemos como funcionam os generators síncronos, vamos ver como funciona as chamadas assíncronas: