>
Rajesh Babu

Follow

> 4 de Maio, 2018 – 9 min ler

>

>

>

>

>
>

>

>

>

>

>

>

>

>>594070>>

>

>

>

>>

>>

>>>>

Se você tem sido um desenvolvedor de JavaScript nos últimos dois a cinco anos você definitivamente terá encontrado posts falando sobre Geradores e Iteradores. Enquanto os Geradores e Iteradores são inerentemente associados, os Geradores parecem um pouco mais intimidadores do que os outros.

>

>

Generator Turbine
>

Iterators são uma implementação de objectos Iterable como mapas, matrizes e strings que nos permite iterar sobre eles usando next(). Eles têm uma grande variedade de casos de uso através de geradores, observáveis e operadores de propagação.

Eu recomendo o seguinte link para aqueles de vocês que são novos em iteradores, Guia para Iteradores.

Para verificar se seu objeto está em conformidade com o protocolo iterável, verifique usando o Symbol.iterator:

Geradores introduzidos como parte do ES6 não sofreram nenhuma alteração para as próximas versões do JavaScript e eles estão aqui para ficar mais tempo. Quero dizer muito tempo! Portanto, não há como fugir dele. Embora ES7 e ES8 tenham algumas novas atualizações, eles não têm a mesma magnitude de mudança que ES6 teve do ES5, que levou o JavaScript para o próximo nível, por assim dizer.

No final deste post, tenho certeza de que você terá um sólido entendimento de como funcionam os Geradores de funções. Se você é um profissional, por favor, ajude-me a melhorar o conteúdo, adicionando seus comentários nas respostas. Só para o caso de terem dificuldade em seguir o código, também adicionei uma explicação para a maioria do código para ajudar a entender melhor.

Funções em JavaScript, como todos sabemos, “run until return/end”. Funções do Gerador, por outro lado, “rodar até rendimento/retorno/fim”. Ao contrário das funções normais Funções do Gerador uma vez chamadas, retorna o Objeto Gerador, que mantém todo o Iterable do Gerador que pode ser iterado usando o método next() ou para…of loop.

Tudo o next() call no gerador executa cada linha de código até o próximo yield que ele encontra e suspende temporariamente sua execução.

Syntacticamente eles são identificados com um *, seja função* X ou função *X, – ambas significam a mesma coisa.

Após criado, a chamada à função do gerador retorna o objeto gerador. Este objeto gerador precisa ser atribuído a uma variável para manter o controle dos métodos subseqüentes de próxima() chamada sobre si mesmo. Se o gerador não for atribuído a uma variável então ele sempre irá render apenas até a primeira expressão de rendimento em cada next().

As funções do gerador são normalmente construídas usando expressões de rendimento. Cada rendimento dentro da função geradora é um ponto de paragem antes do próximo ciclo de execução começar. Cada ciclo de execução é acionado por meio do método next() no generator.

Em cada chamada next(), a expressão yield retorna seu valor na forma de um objeto contendo os seguintes parâmetros.

{ value: 10, done: false } // assuming that 10 is the value of yield

  • Value – é tudo que está escrito no lado direito da palavra-chave yield, pode ser uma chamada de função, objeto ou praticamente qualquer coisa. Para rendimentos vazios este valor é indefinido.
  • Feito – indica o status do gerador, se ele pode ser executado posteriormente ou não. Quando feito retorna verdadeiro, significa que a função terminou a sua execução.

(Se você sentir que está um pouco acima da sua cabeça, você terá mais clareza uma vez que você veja o exemplo abaixo…)

Função básica do gerador

Nota: No exemplo acima a função geradora acessada diretamente sem invólucro é sempre executada apenas até o primeiro rendimento. Portanto, por definição você precisa atribuir o Gerador a uma variável para iterar corretamente sobre ela.

Ciclo de vida de uma Função Geradora

Antes de prosseguirmos, vamos dar uma rápida olhada no diagrama de blocos do ciclo de vida da Função Geradora:

>

Ciclo de vida da função Gerador
A cada vez que é encontrado um rendimento, a função Gerador retorna um objeto contendo o valor do rendimento encontrado e o status feito. Da mesma forma, quando um retorno é encontrado, obtemos o valor de retorno e também o status done como verdadeiro. Sempre que, o status done é retornado como true, significa essencialmente que a função do gerador completou sua execução, e nenhum rendimento adicional é possível.

Todos os dados após o primeiro retorno são ignorados, incluindo outras expressões de rendimento.

Leia mais para entender melhor o diagrama de blocos.

Atribuir rendimento a uma variável

Na amostra de código anterior vimos uma introdução à criação de um gerador básico com um rendimento. E obtivemos o rendimento esperado. Agora, vamos assumir que atribuímos toda a expressão de rendimento a uma variável no código abaixo.

Atribuindo rendimento a uma variável
>

Qual é o resultado de toda a expressão de rendimento passada para a variável ? Nada ou Indefinido …

Porquê ? A partir do segundo próximo(), o rendimento anterior é substituído por argumentos passados na função seguinte. Como, não passamos nada aqui no método seguinte, é assumido que toda a ‘expressão de rendimento anterior’ como indefinida.

Tendo isto em mente, vamos saltar para a próxima secção para entender mais sobre passar argumentos para o método next().

Passar Argumentos para o método next() Método

Com referência ao diagrama de blocos acima, vamos falar sobre passar argumentos para a próxima função. Esta é uma das partes mais complicadas de toda a implementação do gerador.

Consideremos a seguinte parte de código, onde o rendimento é atribuído a uma variável, mas desta vez passamos um valor no método next().

Vejamos o código abaixo no console. E a explicação logo a seguir.

>

>

Passar argumentos para o próximo()

Explicação:

  1. Quando chamamos o primeiro próximo(20), todas as linhas de código até o primeiro rendimento são impressas. Como não temos nenhuma expressão de rendimento anterior, este valor 20 é descartado. Na saída obtemos o valor de rendimento como i*10, que é 100 aqui. Também o estado da execução pára com o primeiro yield e a const j ainda não está definida.
  2. A segunda chamada next(10), substitui toda a primeira expressão de yield por 10, imagine yield (i * 10) = 10, que passa a definir o valor da const j para 50 antes de retornar o valor do segundo yield. O valor de yield aqui é 2 * 50 / 4 = 25.
  3. Terceiro next(5), substitui todo o segundo yield por 5, trazendo o valor de k para 5. E ainda continua a executar declaração de retorno e retorno (x + y + z) => (10 + 50 + 5) = 65 como o valor de rendimento final, juntamente com true.

Isso pode ser um pouco avassalador para os leitores da primeira vez, mas leva uns bons 5 minutos para ser lido repetidamente para entender completamente.

Passing Yield as an Argument of a Function

Existem n-número de casos de uso em torno do yield em relação a como ele pode ser usado dentro de um gerador de funções. Vejamos o código abaixo para um uso tão interessante de yield, juntamente com a explicação.

>

>Yield como argumento de uma função

Explicação

  1. O primeiro próximo() rende valor indefinido porque a expressão yield não tem valor.
  2. O segundo next() produz “I am usless”, o valor que foi passado. E prepara o argumento para a função call.
  3. A terceira next(), chama a função com um argumento indefinido. Como mencionado acima, o método next() chamado sem nenhum argumento significa essencialmente que toda a expressão de rendimento anterior é indefinida. Portanto, isto imprime indefinido e termina a execução.

Yield com uma chamada de função

Uma parte do yield de valores retornados também pode chamar funções e retornar o valor ou imprimir o mesmo. Vamos olhar o código abaixo e entender melhor.

>

Rendimento chamando uma função

O código acima retorna a obj de retorno da função como o valor de rendimento. E termina a execução definindo indefinido para o usuário constante.

Yield with Promises

Yield with promises segue a mesma abordagem da chamada da função acima, ao invés de retornar um valor da função, ele retorna uma promessa que pode ser avaliada posteriormente para sucesso ou fracasso. Vamos ver o código abaixo para entender como funciona.

>

Rendimento com promessas

A apiCall retorna as promessas como o valor de rendimento, quando resolvido após 2 segundos imprime o valor que precisamos.

Yield*

Até agora temos estado a olhar para os casos de uso da expressão yield, agora vamos olhar para outra expressão chamada yield*. Yield* quando usado dentro de uma função geradora delega outra função geradora. Simplificando, ele completa de forma síncrona a função geradora em sua expressão antes de passar para a próxima linha.

Vejamos o código e a explicação abaixo para entender melhor. Este código é do MDN web docs.

Basic yield*

Explicação

  1. A primeira chamada() seguinte produz um valor de 1.
  2. A segunda próxima() chamada, entretanto, é uma expressão yield*, o que inerentemente significa que vamos completar outra função do gerador especificada na expressão yield* antes de continuar a função do gerador atual.
  3. Na sua mente, você pode supor que o código acima é substituído como o abaixo
function* g2() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
}

Isso irá continuar para terminar a execução do gerador. No entanto, existe uma habilidade distinta do rendimento* que você deve ter em mente enquanto usa o retorno, na próxima seção.

Yield* com Return

Yield* com um retorno comporta-se de forma um pouco diferente do rendimento normal*. Quando o yield* é usado com uma declaração de retorno ele avalia para esse valor, significando que toda a função yield*() torna-se igual ao valor retornado da função do gerador associado.

Vejamos o código e a explicação abaixo para entendê-lo melhor.

>

Rendimento* com retorno
>

Explicação

  1. No primeiro próximo() vamos diretamente para o rendimento 1 e retornamos o seu valor.
  2. A segunda seguinte() rende 2.
  3. A terceira seguinte(), retorna ‘foo’ e segue para render o ‘fim’, atribuindo ‘foo’ ao resultado constante no caminho.
  4. A última next() termina a execução.

Yield* com um Objecto Iterável Incorporado

Há mais uma propriedade de yield* interessante, semelhante ao valor de retorno, yield* também pode iterar sobre objectos iteráveis como Array, String e Map.

Vejamos como funciona em tempo real.

Rendimento sobre iterables incorporados

Aqui no código o rendimento* itera sobre cada objeto iterável possível que é passado como sua expressão. Acho que o código em si é auto-explicativo.

Best Practices

Além de tudo isso, todo iterador/gerador pode ser iterado sobre um for…de loop. Similar ao nosso método next() que é chamado explicitamente, for…of loop passa internamente para a próxima iteração com base na palavra-chave yield. E itera apenas até o último rendimento e não processa as declarações de retorno como o método next().

Pode verificar o mesmo no código abaixo.

Rendimento com para…de

O valor de retorno final não é impresso, porque para…de iteração de laço só se faz até o último rendimento. Portanto, vem sob a melhor prática para evitar declarações de retorno dentro de uma função geradora, pois isso afetaria a reusabilidade da função quando iterada sobre um for…of.

>

>

>

>

>

Conclusão

Espero que isto cubra os casos de uso básico das funções do gerador e espero sinceramente que tenha dado uma melhor compreensão de como os geradores funcionam em JavaScript ES6 e acima. Se você gosta do meu conteúdo por favor deixe 1, 2, 3 ou até 50 palmas :).

Por favor siga-me na minha conta GitHub para mais projetos JavaScript e Full-Stack:

Deixe uma resposta

O seu endereço de email não será publicado.