Aguarde...

22 de abril de 2021

Funções do gerador de JavaScript simplificadas

Funções do gerador de JavaScript simplificadas

As funções do gerador são uma daquelas coisas que você pode não ver com tanta frequência. No entanto, eles podem ser úteis em algumas situações. Este tutorial ajudará você a entendê-los. Você aprenderá sobre o que são as funções do gerador. Você também vai aprender sobre yieldnext()e também como delegar a execução.

De funções regulares a funções de gerador

Funções regulares existem desde o início do JavaScript. As funções são um dos blocos de construção fundamentais desta linguagem de programação. Isso não é verdade se falamos sobre funções geradoras. Essas funções especiais foram introduzidas no JavaScript recentemente.

As funções têm funcionado muito bem. Além das funções regulares, agora também existem funções de seta. Até agora, as funções das setas provaram ser benéficas em alguns casos. Freqüentemente, eles também são preferidos por desenvolvedores de JavaScript em relação ao normal. Alguém pode perguntar: por que adicionar algo novo?

As funções regulares e de seta são ótimas quando você deseja encapsular um trecho de código para torná-lo reutilizável. Eles também permitem que você retorne um único valor ou nada. Um único pode ser também uma matriz ou um objeto que contém vários valores. No entanto, ainda há uma coisa que você deseja devolver.

É aqui que as funções do gerador são diferentes. Ao contrário das funções regulares, os geradores são capazes de retornar vários valores. Dito isso, eles não devolvem todos ao mesmo tempo. Em vez disso, eles retornaram um após o outro, apenas quando você quiser. Até então, o gerador irá esperar enquanto lembra do último valor.

A sintaxe

Uma coisa boa sobre geradores é que eles têm uma sintaxe amigável. Há muito o que aprender, especialmente se você já conhece alguma coisa sobre funções regulares . Neste monumento, existem duas maneiras de criar uma função de gerador. O primeiro é com a ajuda do construtor GeneratorFunction .

Essa abordagem não é muito comum e você a verá muito raramente. A segunda abordagem, e mais comum, é usar a declaração de função . Sim, você também pode criar geradores com expressão de função. Em ambos os casos, a functionpalavra-chave é seguida por um asterisco ( *).

Este símbolo é o que diz ao JavaScript que você deseja criar uma função geradora, não uma função regular. Exceto esta pequena mudança, o resto da sintaxe é o mesmo que para a função. Há o nome da função, parênteses para parâmetros e corpo da função com o código a ser executado.

// Create generator function:
function* myGenerator() {
  // Function body with code to execute.
}

O objeto gerador

Uma coisa que pode surpreendê-lo é que os geradores não executam o código dentro deles quando você os chama. Chamar uma função do gerador não executa o código dentro dela. Em vez disso, a função do gerador retornará um objeto especial denominado “objeto gerador”. Este objeto permite que você trabalhe com o gerador.

Ele permite que você diga ao gerador para retornar um novo valor quando você precisar dele, referenciando este objeto. Por causa disso, ao chamar uma função de gerador, você deve atribuir o objeto gerador retornado a alguma variável. Caso contrário, ele será perdido.

// Create generator function:
function* myGenerator() {
  // Function body with code to execute.
}

// Assign the generator object to variable:
const myGeneratorObj = myGenerator()

// Log the generator object:
console.log(myGeneratorObj)
// Output:
// Iterator [Generator] {}

O rendimento e o próximo ()

Quando se trata de funções geradoras, há duas coisas importantes. A primeira é a palavra-chave yield , junto com alguma expressão. O segundo é o next()método. A yieldpalavra-chave é como um ponto de interrupção. Você pode usá-lo apenas em uma função de gerador. Ele faz duas coisas. Primeiro, ele retorna um valor do gerador.

A segunda coisa que ele faz é pausar a execução do gerador. Isso acontece logo depois que o gerador retorna o valor. Você pode pensar no yieldcomo uma returndeclaração. A diferença é que enquanto o returnretorna e termina uma função, yieldretorna e apenas pausa um gerador.

Como você sabe, chamar uma função de gerador retorna um objeto de gerador. O next()é o método principal deste objeto gerador. Este método permite que você execute o gerador, execute o código interno e retorne algum valor. O valor que ele retorna é especificado pela yieldpalavra – chave. É precedido por ele.

Então, para resumir. O yieldpermite que você retorne um valor do gerador, ao executá-lo, e então pause o gerador. O next()método permite que você execute o gerador, retorne o valor que segue após o yield. Lembre-se que next()retornará apenas o valor após o primeiro yield.

Se você usar cinco yieldpalavras-chave em um gerador, terá que chamar o next()método cinco vezes. Uma chamada para uma yield. A cada rendimento, a execução de um gerador será pausada, aguardando outra chamada de next()método.

// Create generator function:
function* myGenerator() {
  // Use yield to return values:
  yield 1
  yield 2
  yield 3
  yield 4
  return 5
}

// Assign the generator object to variable:
const myGeneratorObj = myGenerator()

// Return the first value:
console.log(myGeneratorObj.next())
// Output:
// { value: 1, done: false }

// Return the second value:
console.log(myGeneratorObj.next())
// Output:
// { value: 2, done: false }

// Return the third value:
console.log(myGeneratorObj.next())
// Output:
// { value: 3, done: false }

// Return the fourth value:
console.log(myGeneratorObj.next())
// Output:
// { value: 4, done: false }

// Return the fifth value:
console.log(myGeneratorObj.next())
// Output:
// { value: 5, done: true }
// The generator is finished.

// Try to return one more time:
console.log(myGeneratorObj.next())
// Output:
// { value: undefined, done: true }

Rendimento, próximo, valor e pronto

Quando você chama o next()método, o JavaScript sempre retorna um objeto. Este objeto conterá duas propriedades com alguns valores. Uma propriedade será value. Este é o valor real retornado pelo gerador. É o valor que segue após a yieldpalavra – chave em seu código.

Se não houver valor a ser retornado, o valor dessa propriedade será undefined. A segunda propriedade é done. Esta propriedade informa se a função do gerador foi concluída ou não. “Concluído” significa que não há mais yieldpalavras – chave nas funções do gerador e não há mais valores a serem retornados.

O valor de donesempre será um booleano, trueou false. Será falseaté que o gerador alcance o último yield. Quando isso acontecer, ele retornará o último valor, após o último yield, junto com donedefinido como true. Depois disso, ligar next()novamente será inútil.

// Create generator function:
function* myGenerator() {
  // Use yield to return values:
  yield 'a'
  yield 'b'
  return 'omega'
}

// Assign the generator object to variable:
const myGeneratorObj = myGenerator()

// Return the first value:
console.log(myGeneratorObj.next())
// Output:
// { value: 'a', done: false }

// Return the second value:
console.log(myGeneratorObj.next())
// Output:
// { value: 'b', done: false }

// Return the third value:
console.log(myGeneratorObj.next())
// Output:
// { value: 'omega', done: true }
// This is the last value returned
// and the generator is finished.

Rendimento e retorno

Só porque os geradores costumam yieldretornar valores não significa que não haja lugar para returndeclarações. Ainda existe. No contexto das funções geradoras, uma returninstrução é outra coisa que especifica se o gerador foi concluído. O gerador terminará em duas condições.

Primeiro, não há mais yieldpalavras-chave. Em segundo lugar, a execução encontra uma returndeclaração. Esses dois irão alterar o valor de doneno objeto retornado de falsepara true. Retornar um valor com retorno funciona assim yield. Qualquer valor após o returnserá o valor da valuepropriedade no objeto retornado.

Três coisas para lembrar. Primeiro, returnvai terminar o gerador quer haja outro yieldou não. Digamos que você declare quatro yieldsem um gerador, mas coloque returnapós o segundo. O resultado será que o gerador retornará três valores. Dois valores para os dois primeiros yielde um para o return.

Os dois últimos yieldapós a instrução de retorno nunca serão executados porque o returnterminará o gerador prematuramente. A segunda coisa a lembrar é que você não precisa necessariamente usar a returninstrução. O gerador terminará quando encontrar o último yield.

O terceiro para lembrar. Se você não usar return, o valor doneapós o último yieldainda será definido como false. Ele mudará apenas se você tentar retornar um valor mais uma vez. Com returndoneserá definido como truecorreto com a última chamada de next()método.

// Generator function without return:
// NOTE: last yield will not change "done" to "true".
// It will change only after another call of "next()".
function* myGeneratorOne() {
  // Use yield to return values:
  yield 'a'
  yield 'b'
}

// Assign the generator object to variable:
const myGeneratorOneObj = myGeneratorOne()

// Return the first value:
console.log(myGeneratorOneObj.next())
// Output:
// { value: 'a', done: false }

// Return the second value:
console.log(myGeneratorOneObj.next())
// Output:
// { value: 'b', done: false }

// Try to return value again:
console.log(myGeneratorOneObj.next())
// { value: undefined, done: true }
// The generator is finished.


// Generator function ending with return:
// NOTE: the return will change "done" to "true" right away.
function* myGeneratorOne() {
  // Use yield to return values:
  yield 'a'
  return 'b'
}

// Assign the generator object to variable:
const myGeneratorOneObj = myGeneratorOne()

// Return the first value:
console.log(myGeneratorOneObj.next())
// Output:
// { value: 'a', done: false }

// Return the second value:
console.log(myGeneratorOneObj.next())
// Output:
// { value: 'b', done: true }
// The generator is finished.


// Generator function with return in the middle:
function* myGeneratorOne() {
  // Use yield to return values:
  yield 'a'
  yield 'b'
  return 'End'
  yield 'c'
  yield 'd'
}

// Assign the generator object to variable:
const myGeneratorOneObj = myGeneratorOne()

// Return the first value:
console.log(myGeneratorOneObj.next())
// Output:
// { value: 'a', done: false }

// Return the second value:
console.log(myGeneratorOneObj.next())
// Output:
// { value: 'b', done: false }

// Return the third value (the return):
console.log(myGeneratorOneObj.next())
// Output:
// { value: 'End', done: true }
// The generator is finished.

// Try to return the fourth value:
console.log(myGeneratorOneObj.next())
// Output:
// { value: undefined, done: true }

Exemplo de função do gerador com um loop

Essa capacidade de retornar um valor sob demanda pode ser útil, por exemplo, quando você deseja gerar uma série de números com loop. Normalmente, o loop retornaria todos os números imediatamente. Isso não acontecerá se você usar funções geradoras. Os geradores permitirão que você retorne todos os números um por um.

Existem apenas algumas coisas que você precisa para criar este gerador de números. Primeiro, é uma função geradora. Dentro deste gerador haverá um loop. Dentro desse loop estará a yieldpalavra – chave que retorna o número atual da série. Isso criará um loop que fará uma pausa após cada iteração, aguardando a próxima chamada de next().


// Example of generator with for loop: function* myGenerator() { for (let i = 0; i < 5; i++) { yield i } } // Assign the generator object to variable: const myGeneratorObj = myGenerator() // Return the first number: console.log(myGeneratorObj.next()) // Output: // { value: 0, done: false } // Return the second number: console.log(myGeneratorObj.next()) // Output: // { value: 1, done: false } // Return the third number: console.log(myGeneratorObj.next()) // Output: // { value: 2, done: false } // Return the fourth number: console.log(myGeneratorObj.next()) // Output: // { value: 3, done: false } // Return the fifth number: console.log(myGeneratorObj.next()) // Output: // { value: 4, done: false } // Try to return another number: console.log(myGeneratorObj.next()) // Output: // { value: undefined, done: true } // The generator is finished.

Rendimento * e delegação de execução

yieldpalavra-chave pode fazer mais do que apenas retornar um valor. Ele também permite que você delegue a execução do gerador a outro gerador. Você pode usá-lo para iniciar um segundo gerador do primeiro. Este segundo gerador funcionará até que seja concluído. Em seguida, a execução continuará para o primeiro gerador.

Isso permite que você conecte vários geradores. Em seguida, você pode executá-los em uma série que pode controlar, cada um pelo tempo necessário. Quando você quiser usar o yieldpara fazer isso, lembre-se de adicionar o símbolo de asterisco ( *) logo após a yieldpalavra – chave ( yield*). Em seguida, adicione uma chamada ao gerador que deseja executar.

// Create first generator function:
function* myGeneratorOne() {
  yield 1
  yield* myGeneratorTwo() // Delegate to myGeneratorTwo() generator.
  yield 3
}

// Create second generator function:
function* myGeneratorTwo() {
  yield 'a'
  yield 'b'
  yield 'c'
}

// Assign the first generator object to variable:
const myGeneratorObj = myGeneratorOne()

// Return the first value (myGeneratorOne):
console.log(myGeneratorObj.next())
// Output:
// { value: 1, done: false }

// Return the second value (myGeneratorTwo):
console.log(myGeneratorObj.next())
// Output:
// { value: 'a', done: false }

// Return the third value (myGeneratorTwo):
console.log(myGeneratorObj.next())
// Output:
// { value: 'b', done: false }

// Return the fourth value (myGeneratorTwo):
console.log(myGeneratorObj.next())
// Output:
// { value: 'c', done: false }

// Return the fifth value (myGeneratorOne):
console.log(myGeneratorObj.next())
// Output:
// { value: 3, done: false }

// Return the sixth value (myGeneratorOne):
console.log(myGeneratorObj.next())
// Output:
// { value: undefined, done: true }

Rendimento * e declaração de retorno

Quando você usa a delegação, cuidado com as returndeclarações. Isso se aplica especialmente a geradores em algum lugar da série. Não se preocupe. A returndeclaração não vai terminar, ou encerrar, toda a cadeia. Ele só vai terminar o gerador em que está. No entanto, ele não retornará nenhum valor.

Quando você usa a returninstrução em um gerador, ele encerrará o gerador. Ele também retornará um valor que o segue. Isso não se aplica à execução delegada e à cadeia de geradores. Neste caso, returnapenas finalizará o gerador de corrente e retomará a execução do anterior. Não retornará um valor.

// Create first generator function:
function* myGeneratorOne() {
  yield 1
  yield* myGeneratorTwo() // Delegate to myGeneratorTwo() generator.
  yield 3
}

// Create second generator function:
function* myGeneratorTwo() {
  yield 'a'
  yield 'b'
  return 'c' // This returned value will not show up.
}

// Assign the first generator object to variable:
const myGeneratorObj = myGeneratorOne()

// Return the first value (myGeneratorOne):
console.log(myGeneratorObj.next())
// Output:
// { value: 1, done: false }

// Return the second value (myGeneratorTwo):
console.log(myGeneratorObj.next())
// Output:
// { value: 'a', done: false }

// Return the third value (myGeneratorTwo):
console.log(myGeneratorObj.next())
// Output:
// { value: 'b', done: false }

// Return the fourth value (myGeneratorOne):
console.log(myGeneratorObj.next())
// Output:
// { value: 3, done: false }

// Return the fifth value (myGeneratorOne):
console.log(myGeneratorObj.next())
// Output:
// { value: undefined, done: true }

Yield, next () e passando argumentos

Há uma coisa interessante sobre o next()método. Ele permite que você passe argumentos para funções geradoras. Quando você passa algo como um argumento para o next(), esse valor se tornará o valor de yieldno gerador. Dito isso, se você quiser passar algum argumento, faça isso na segunda chamada de next(), não na primeira.

A razão para isso é simples. A primeira chamada do next()método inicia a execução do gerador. O gerador é então pausado quando atinge o primeiro yield. Não existe yieldentre o início da execução do gerador e o primeiro yield. Portanto, qualquer argumento que você passar será perdido.

// Create generator function:
function* myGenerator() {
  console.log(yield + 1)
  console.log(yield + 2)
  console.log(yield + 3)
  console.log(yield + 4)
  return 5
}

// Assign the first generator object to variable:
const myGeneratorObj = myGenerator()

// Return the first value (no argument passing):
console.log(myGeneratorObj.next())
// Output:
// { value: 1, done: false }
// '1x' // <= value from console.log(yield + ...)

// Return the second value:
console.log(myGeneratorObj.next('1x'))
// Output:
// { value: 2, done: false }
// '2x' // <= value from console.log(yield + ...)

// Return the third value:
console.log(myGeneratorObj.next('2x'))
// Output:
// { value: 3, done: false }
// '3x' // <= value from console.log(yield + ...)

// Return the fourth value:
console.log(myGeneratorObj.next('3x'))
// Output:
// { value: 4, done: false }
// '4x' // <= value from console.log(yield + ...)

// Return the fifth value:
console.log(myGeneratorObj.next('4x'))
// Output:
// { value: 5, done: true }

Conclusão: Funções do gerador de JavaScript simplificadas

As funções do gerador podem não ser usadas com tanta frequência, mas podem ser úteis. Por exemplo, quando você deseja gerar alguns dados sob demanda. Ou, quando você deseja ter mais controle sobre a iteração de alguns dados. Espero que este tutorial tenha ajudado você a entender o que são funções geradoras e como trabalhar com elas.

Postado em Blog
Escreva um comentário