Generator function no Javascript

đ Sobre
Geradores são funçÔes cuja execução pode ser interrompida e posteriormente reconduzida. Seus contextos (de associaçÔes de variåveis) ficarão salvos entre cada recondução. A declaração function* (palavra chave function seguida de um asterisco) define uma função geradora que retorna um objeto Generator.
đ Alguns exemplos da função
â FunçÔes geradoras sĂŁo pilares para uma programação assĂncrona que mitigam os problemas com callbacks. Ao invocar uma função geradora sua implementação nĂŁo Ă© executada imediatamente. Ao invĂ©s disso Ă© retornado um iterator.
function* proximoID() {
let index = 0
while (true) yield index++
}
const generatorID = proximoID()
console.log(gen.next().value) // 0
console.log(gen.next().value) // 1
console.log(gen.next().value) // 2
â Yield define o valor que retorna de uma generator function via o protocolo iterator. Se omitido, serĂĄ retornado undefined. Se return(value) for chamado em um gerador que jĂĄ estĂĄ no estado “concluĂdo”, o gerador permanecerĂĄ no estado “concluĂdo”. Se nenhum argumento for fornecido, a propriedade value do objeto retornado serĂĄ “indefinida”. Se um argumento for fornecido, ele serĂĄ definido como o valor da propriedade value do objeto retornado.
function* gen() {
yield 1
yield 2
yield 3
}
const g = gen()
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
g.return() // { value: undefined, done: true }
g.return(1) // { value: 1, done: true }
â Ao chamar a função next() passando um valor como parĂąmetro a função interna do gerador pode atribuir a esse valor.
function* fib() {
let a = 1
let b = 1
while (true) {
let curr = b
b = a
a = a + curr
const reset = yield curr
if (reset) a = b = 1
}
}
const sequence = fib()
console.log(sequence.next().value) //1
console.log(sequence.next().value) //1
console.log(sequence.next().value) //2
console.log(sequence.next().value) //3
console.log(sequence.next().value) //5
console.log(sequence.next(true).value) //1
console.log(sequence.next().value) //1
console.log(sequence.next().value) //2
console.log(sequence.next().value) //3
â Ă importante notar que o primeiro next() que Ă© chamado nĂŁo passarĂĄ um valor. Ele apenas iniciarĂĄ o gerador. Para demonstrar isso, podemos registrar o valor do yield e chamar o next() algumas vezes com alguns valores.
function* generatorFunction() {
console.log(yield)
console.log(yield)
return 'The end'
}
const generator = generatorFunction()
generator.next()
generator.next(100)
generator.next(200)
Isso darĂĄ o seguinte resultado:
Output
100
200
{value: "The end", done: true}
â Generators computam seus valores “yielded” por demanda, que os permitem representar sequĂȘncias de forma eficiente que costumam ser trabalhosas ao serem computadas, ou atĂ© sequĂȘncias infinitas. Aqui um gerador de sequĂȘncia Fibonacci usando next(x) pra restartar a sequĂȘncia. A SequĂȘncia de Fibonacci Ă© uma sequĂȘncia numĂ©rica infinita que foi elaborada pelo matemĂĄtico italiano Leonardo Pisa. Começando pelo nĂșmero 1, a sequĂȘncia Ă© formada pela soma de cada numeral com o nĂșmero que o antecede. Ou seja, 1 + 1 = 2, 2 + 1 = 3, 3 + 2 = 5, e assim por diante.
function* fibonacci() {
var fn1 = 1
var fn2 = 1
while (true) {
var current = fn2
fn2 = fn1
fn1 = fn1 + current
var reset = yield current
if (reset) {
fn1 = 1
fn2 = 1
}
}
}
var sequence = fibonacci()
console.log(sequence.next().value) // 1
console.log(sequence.next().value) // 1
console.log(sequence.next().value) // 2
console.log(sequence.next().value) // 3
console.log(sequence.next().value) // 5
console.log(sequence.next().value) // 8
console.log(sequence.next().value) // 13
console.log(sequence.next(true).value) // 1
console.log(sequence.next().value) // 1
console.log(sequence.next().value) // 2
console.log(sequence.next().value) // 3
đ for..of
Com especificaçÔes do ES6 podemos utilizar o loop do for..of para iterar sobre um gerador. O iterator criado pela função geradora Ă© capturada pelo for e automaticamente iterada sobre seus valores atĂ© encontrar o done. O valor na Ășltima iteração tambĂ©m nĂŁo Ă© executa jĂĄ que a condição done=true foi satisfeita na Ășltima iteração do laço.
function* foo() {
yield 1
yield 3
yield 4
return 8
}
for (let v of foo()) console.log(v) // 1234
â Sabemos que para salvar na base Ă© um processo assĂncrono, entĂŁo se vocĂȘ utilizar um looping convencional, um for, forEach, map, vocĂȘ notarĂĄ que todos registros vĂŁo ser processados quase ao mesmo tempo, gerando assim uma grande demanda do processador e memĂłria, jĂĄ com o generator, vocĂȘ consegue processar individualmente cada item da lista e esperar pelo retorno de cada um, assim vocĂȘ consegue controlar como vai ser percorrido essa lista.
/*
* looping convencional
*/
const saveUser = async name => {
console.log('Iniciado processo para salvar usuĂĄrio: ', name)
const timeToAwait = Math.floor(Math.random() * (5000 - 2000)) + 2000
return new Promise(resolve =>
setTimeout(() => {
console.log(`UsuĂĄrio ${name} salvo`)
resolve()
}, timeToAwait)
)
}
const users = ['Roberto', 'Dirceu', 'Pablo', 'Anderson']
users.map(saveUser)
/*
Iniciado processo para salvar usuĂĄrio: Roberto
Iniciado processo para salvar usuĂĄrio: Dirceu
Iniciado processo para salvar usuĂĄrio: Pablo
Iniciado processo para salvar usuĂĄrio: Anderson
UsuĂĄrio Dirceu salvo
UsuĂĄrio Pablo salvo
UsuĂĄrio Roberto salvo
UsuĂĄrio Anderson salvo
*/
/*
* looping usando Generator
*/
const saveUser = async name => {
console.log('Iniciado processo para salvar usuĂĄrio: ', name)
/*
* Tempo aleatĂłrio para simbolizar um processo assĂncrono
*/
const timeToAwait = Math.floor(Math.random() * (5000 - 2000)) + 2000
/*
* Simulando um processo assĂncrono ao salvar o usuĂĄrio na base de dados
*/
return new Promise(resolve =>
setTimeout(() => {
console.log(`UsuĂĄrio ${name} salvo`)
resolve()
}, timeToAwait)
)
}
const users = ['Roberto', 'Dirceu', 'Pablo', 'Anderson']
function* createGenerator(list) {
yield* list
}
const usersGenerator = createGenerator(users)
for await (let user of usersGenerator) {
await saveUser(user)
}
/*
Iniciado processo para salvar usuĂĄrio: Roberto
UsuĂĄrio Roberto salvo
Iniciado processo para salvar usuĂĄrio: Dirceu
UsuĂĄrio Dirceu salvo
Iniciado processo para salvar usuĂĄrio: Pablo
UsuĂĄrio Pablo salvo
Iniciado processo para salvar usuĂĄrio: Anderson
UsuĂĄrio Anderson salvo
*/
đ Done
Como percebemos acima usamos o Done ao fim de qualquer uma das funçÔes. Onde nenhuma das expressÔes podem ser encontradas o generator emite um {done: true}. Uma vez que {done:true} foi emitido, todas as chamadas subsequentes a g.next() vão ser completamente ignoradas e o generator vai retornar {done:true}, pois é o done que diz se acabou os passos do generator.
function* generator() {
yield 'yeah'
}
var g = generator()
console.log(g.next()) // { done: false, value: 'yeah' }
console.log(g.next()) // { done: true }
console.log(g.next()) // { done: true }
đ ReferĂȘncias
VocĂȘ pode estudar um pouco mais do assunto nos links:
Se vocĂȘ curtiu esse conteĂșdo deixe o seu like e compartilhe com a galera. đ
comments powered by DisqusDirceu Heineck.