Generators Functions - o que acontece por baixo dos panos

Escrito por

Hoje vamos continuar falando sobre Generators Functions em JavaScript.

O que é Generator?

É uma função que podemos pausar ou iterar sobre ela. Já falamos sobre generators no hands-on do Módulo CO, que resolve um generator, mas hoje vamos falar como ele funciona ‘por baixo dos panos’ e até mesmo como o CO foi construído.

A primeira coisa que temos que saber é que a sintaxe do generator é criada com uma function e um asterisco. Se fizermos isso, dizemos que é uma função generator, então é possível pausar essa função com CO, o que deixa o código mais linear:

1function* generator() {
2 console.log("Entrou no generator")
3}
4const gen = generator()

Ao executar o código, perceba que não saiu nada. Primeiro precisamos iterar sobre ele para que consigamos ter um valor, para isso criamos um:

1const iteration = gen.next()

O next é muito comum em outras linguagens, nas quais temos algumas estruturas que são iteráveis. Enquanto você utilizar o .next significa que você ainda tem dados para iterar.

Agora executamos para ver o que acontece novamente. Perceba que aparece a mensagem (‘Entrou no generator’) porque ele depende do next para entrar na primeira linha.

Agora vamos fazer algo diferente:

1function* generator() {
2 console.log("Entrou no generator")
3 console.log("Segundo passo")
4 console.log("Penultimo passo")
5 console.log("fim do generator")
6}
7const gen = generator()
8const iteration = gen.next()

Ele executou tudo de uma vez. Agora vamos fazer o seguinte:

1yield 'outro valor'

Olha que interessante, ele parou agora no segundo passo, se eu der um console.log no iteration ele retorna um objeto com um value 'outro valor' dentro  e um done: false. Ele parou e retornou esse valor, então ela é uma função que além de iterável, conseguimos ter vários retorno de valor. Se eu quisesse continuar executando, tenho que chamar novamente gen. next:

1function* generator() {
2 console.log("Entrou no generator")
3 console.log("Segundo passo")
4 yield "outro valor"
5 console.log("Penultimo passo")
6 console.log("fim do generator")
7}
8const gen = generator()
9const iteration = gen.next()
10gen.next()

Perceba que ele fez os dois primeiros passos, pausou para retornar o yeild, e por fim fez os dois últimos passos, por isso é chamada de função pausavel.

No yeild, podemos por exemplo retornar uma promise:

1function* generator(){
2 console.log('Entrou no generator')
3 console.log('Segundo passo')
4 yield new Promise((resolve, reject) => {
5 setTimeout(() => resolve(10),  2000
6 })
7 console.log('Penultimo passo')
8 console.log('fim do generator')
9}
10const gen = generator()
11const iteration = gen.next()
12iteration.value.then(() => {
13 gen.next()
14})

Imagine que estamos buscando no banco de dados alguma informação; quando fazemos isso, estamos fazendo uma operação assíncrona e ela vai demorar mais tempo porque é um IO, o yeild está simulando essa busca e o iteration.value.then está esperando essa 'busca' para retornar as próximas informações.

Então o que o CO faz é o controle de chamar o next e executar o then de forma organizada. Vamos supor que esse código fosse um function ReadFilePromise:

1function ReadFilePromise () {
2 return new Promise((resolve, reject) => {
3 setTimeout(() => resolve(10),  2000
4 )})
5}
6function* generator(){
7 console.log('Entrou no generator')
8 console.log('Segundo passo')
9 const value = yield readFilePromise()
10 console.log('Penultimo passo' value)
11 console.log('fim do generator')
12}
13const gen = generator()
14const iteration = gen.next()
15iteration.value.then(()=>{
16 gen.next()
17})

Ao fazer isso, ele vai chamar a nossa function ReadFilePromise e esperar. Para aproximar mais do CO o value tem que vir preenchido, mas perceba que ele veio com undefined porque não aproveitamos o value no then. Então vamos aproveitar:

1const gen = generator()
2const iteration = gen.next()
3iteration.value.then(val => {
4 gen.next(val)
5})

Assim vai ser retornado o penúltimo com o valor 10. O CO faz exatamente isso, conseguimos transformar facilmente em uma função genérica que resolva qualquer generator, por isso é tão legal resolver com promise, porque se fosse com readFile comum a gente não tem esse retorno de valor. Confira a dica em vídeo:

Curta o DevPleno no Facebookinscreva-se no canal e não se esqueça de cadastrar seu e-mail para não perder as novidades. Abraço!

Evolua mais rápido

Junte-se a milhares de desenvolvedores no nosso time de alunos premium e alcance mais rápido o próximo nível da sua carreira.

Ver cursos Premium