Geradores: os bits retorcidos

O esboço da especificação ECMAScript 6 (link em inglês) já rendeu muitas alegria para o desenvolvedor JavaScript moderno. Abordamos algumas novas classes de coleções e loops de iteração de for..of em uma postagem anterior. Nesta postagem, vamos falar sobre algo que anda de mãos dadas com loops for..of: funções geradoras.

Já existe um host de materiais incríveis que explica por que e como usar geradores. Em resumo, os geradores são funções especiais que criam iteradores, e os iterações 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 para next(). O uso de yield suspende a execução da função do gerador, preservando o estado até que next() seja chamado novamente. Nesse momento, o código começa a voltar e continua até gerar outro valor (yield) ou até que a função do gerador seja encerrada. Há vários casos de uso canônicos para funções geradoras, como usá-las para iterar os números na sequência de Fibonacci (link em inglês).

Agora que já falamos do básico, vamos nos aprofundar com um exemplo de JavaScript que abrange alguns dos "pegadinhos", ou "partes arrepiantes", de trabalhar com geradores. Há vários comentários por toda parte, 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, construir um gerador resulta em um iterador exclusivo com o próprio estado distinto, e você pode transmitir parâmetros para o 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 iteração, e esse valor será atribuído ao que estiver no lado esquerdo da instrução yield na invocação anterior. 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, use um parâmetro para o construtor do gerador.

Por fim, os geradores podem produzir iterações finitas ou infinitas. Se você estiver trabalhando com um iteração infinito, verifique se há algum tipo de condição do terminal com base no valor yielded. É muito fácil escrever loops infinitos por acidente, especialmente ao usar for..of para iteração. Se você estiver trabalhando com um iteração finito usando chamadas para next(), a propriedade .done do objeto retornado vai indicar se a iteração está concluída.

Esperamos que esta amostra, junto com os outros recursos disponíveis na Web, gere empolgação e faça você pensar sobre como usar geradores em seu próprio código. As versões do Firefox a partir da 31 e o Chrome a partir da 39 são compatíveis com os geradores de forma nativa. O projeto Regenerator oferece suporte ao gerador para outros navegadores, e usar o Traceur também é uma opção.

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