Blog / Javascript

Generators Functions - o que acontece por baixo dos panos

TTulio Faria 29 de jun. de 2017 3 min de leitura
Generators Functions - o que acontece por baixo dos panos

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:

function* generator() {
  console.log('Entrou no generator')
}
const 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:

const 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:

function* generator() {
  console.log('Entrou no generator')
  console.log('Segundo passo')
  console.log('Penultimo passo')
  console.log('fim do generator')
}
const gen = generator()
const iteration = gen.next()

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

yield '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:

function* generator() {
  console.log('Entrou no generator')
  console.log('Segundo passo')
  yield 'outro valor'
  console.log('Penultimo passo')
  console.log('fim do generator')
}
const gen = generator()
const iteration = gen.next()
gen.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:

function* generator(){
    console.log('Entrou no generator')
    console.log('Segundo passo')
    yield new Promise((resolve, reject) => {
        setTimeout(() => resolve(10),  2000
    })
    console.log('Penultimo passo')
    console.log('fim do generator')
}
const gen = generator()
const iteration = gen.next()
iteration.value.then(() => {
    gen.next()
})

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:

function ReadFilePromise () {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(10),  2000
    )})
}
function* generator(){
    console.log('Entrou no generator')
    console.log('Segundo passo')
    const value = yield readFilePromise()
    console.log('Penultimo passo' value)
    console.log('fim do generator')
}
const gen = generator()
const iteration = gen.next()
iteration.value.then(()=>{
    gen.next()
})

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:

const gen = generator()
const iteration = gen.next()
iteration.value.then((val) => {
  gen.next(val)
})

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 Facebook, inscreva-se no canal e não se esqueça de cadastrar seu e-mail para não perder as novidades. Abraço!

T
Escrito por
Tulio Faria

Mestre em Sistemas de Informação pela USP e criador do DevPleno. Iniciou sua carreira como professor com apenas 18 anos em um curso técnico, foram 11 anos em sala de aula formando desenvolvedores fullstack no sul de Minas Gerais.

Javascript
Compartilhar X LinkedIn
Continue lendo

Insights relacionados