Geradores: os bits retorcidos

A especificação preliminar do ECMAScript 6 já rendeu muitas fontes de alegria para o desenvolvedor moderno de JavaScript. Cobrimos algumas novas classes de coleções e repetições de iteração for..of em uma postagem anterior. Nesta postagem, falaremos sobre algo que anda de mãos dadas com as repetições for..of: as funções de gerador.

Já temos vários materiais incríveis que abordam o porquê e como usar os geradores. Em resumo, os geradores são funções especiais que criam iteradores, e os iteradores são objetos que têm um método next(), que pode ser chamado para receber um valor. Em uma função de gerador, a palavra-chave yield fornece o valor de next(). O uso de yield suspensa a execução da função de gerador, preservando o estado até que next() seja chamado novamente. Nesse momento, o código começa e continua até yield outro valor (ou até que a função do gerador seja encerrada). Existem vários casos de uso canônicos para funções de gerador, como o uso para iterar os números na sequência de Fibonacci.

Depois de definir o básico, vamos nos aprofundar com uma amostra de JavaScript que aborda alguns dos problemas, ou "penetras desagradáveis", do trabalho com geradores. Há diversos comentários ao longo do conteúdo e você pode testar a versão ativa do código antes de ler:

Quais são as principais conclusões do código?

Primeiro, a criação de um gerador resulta em um iterador exclusivo com o próprio estado distinto, e você pode transmitir parâmetros ao construtor do gerador, que pode controlar o comportamento.

Em segundo lugar, é possível transmitir um parâmetro ao chamar o método next() de um iterador, e esse valor será atribuído ao que estiver no lado esquerdo da instrução yield da invocação anterior do iterador. Essa é uma ótima maneira de variar a saída do iterador. Aqui, nós a usamos para controlar se a palavra gerada está em maiúsculas ou não. Se você quiser influenciar o primeiro valor gerado, faça isso usando um parâmetro para o construtor do gerador.

Por fim, os geradores podem produzir iteradores finitos ou infinitos. Se você estiver trabalhando com um iterador infinito, verifique se tem algum tipo de condição terminal com base no valor yield. É muito fácil escrever loops infinitos acidentalmente, especialmente ao usar for..of para iteração. Se você estiver trabalhando com um iterador finito usando chamadas para next(), a propriedade .done do objeto retornado vai indicar se a iteração foi concluída.

Esperamos que este exemplo, junto com outros recursos disponíveis na Web, gere alguma empolgação e faça você pensar sobre como usar geradores em seu próprio código. Versões do Firefox a partir da versão 31 e do Chrome a partir da 39 oferecem suporte nativo aos geradores. O projeto Regenerator oferece suporte a outros navegadores, e usar o Traceur também é uma opção.

Agradecemos a Erik Arvidsson pela ajuda na revisão deste artigo.