Geralmente não se fala tanto sobre o generators. Isso ocorre porque existem poucos cenários em que você realmente deseja usá-lo.
No entanto, isso não nega a utilidade do recurso - porque quando esses cenários acabam surgindo, você ficará feliz em ter o generator em seu currículo.
Como a maioria das coisas em programação, o
generator
é apenas uma ferramenta; Nesse caso, uma ferramenta especializada.
Vamos dar uma olhada em um exemplo de generator
em ação e percorrer cada etapa e ver como está funcionando:
function* firstGenerator(range){
let i = 0;
while(i < range) {
i+= 1;
yield i;
}
}
Aqui está um generator
definido.
Você perceberá que é muito semelhante a uma função normal, exceto pelo fato de termos um *
e yield
.
O *
informa ao JavaScript que esta é uma função geradora.
Discutiremos o yield
em um minuto.
Primeiro, vamos mostrar um exemplo do gerador em ação:
let iterator = firstGenerator(3)
iterator // Object [Generator] {}
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: undefined, done: true }
Nosso firstGenerator
retornou um iterator
, dando acesso a um próximo método.
Cada vez que chamamos o next
, nossa função executará o código até encontrar uma declaração de rendimento (yield
). Quando encontramos uma declaração de rendimento, interromperemos a execução até que a próxima seja chamada novamente. Quando o someGenerator
terminar a execução, na próxima vez que chamarmos next
, Nós receberemos um objeto que tenha uma chave done
com um valor definido como true
.
Bem maneiro né?
Bem, o retorno do iterator
realmente nos permite fazer mais do que apenas isso.
Também temos acesso a instruções como for… of
, bem como a outros métodos de iteração, como o operador spread
:
let iterator2 = firstGenerator(3);
for( const item of iterator2 ) {
console.log(item)
}
Agora que sabemos o básico sobre o uso de geradores, vamos ver alguns casos de uso.
Um caso de uso comum seria manter o estado de um gerador de ID baseado em um índice.
Digamos que temos um map/object
de itens e queremos expor uma função que permita que um usuário
adicione um item a esse mapa, cada item deve ter um ID exclusivo com base na ordem que foi inserido.
Podemos gerar essa instrução de Geração de IDs usando generators
function* idGenerator(){
let i = 0;
while(true) {
i += 1;
yield `id-${i}`
}
}
const ourItems = {};
const ourIdGenerator = idGenerator();
function addItem(item) {
const id = ourIdGenerator.next()
ourItems[id] = { id, item }
}
addItem('valor a ser guardado')
Outro exemplo seria abstrair o UX Flow em uma única função.
Esse exemplo é de longe o que eu mais gosto.
- Nota do tradutor
Imagine que temos um design de UX; Um usuário clica em um botão, depois fazemos alguns cálculos; depois que os cálculos terminam, queremos mostrar outro botão; depois de clicar nesse botão, fazemos mais alguns cálculos e então atualizamos a janela.
Poderíamos colocar tudo isso em uma única função, mas pode ficar bem confuso.
Em vez disso, como sabemos a ordem na qual nosso design flui, podemos usar generators
:
function* UXDesignFlow(){
yield showFirstButton();
yield showSecondButton();
yield window.location.reload();
}
function mainApp() {
const uxIterator = UXDesignFlow();
uxIterator.next();
firstButton.on('click', () => uxIterator.next() )
secondButton.on('click', () => uxIterator.next() )
}
No exemplo acima, isolamos com êxito nosso fluxo de design e nossa lógica.
Isso facilita o teste, a legibilidade e, consequentemente, a manutenção.
Cada vez que concluímos nossos cálculos, mostramos a próxima etapa na interface do usuário.
Conclusão
Na maioria das vezes você não vai precisar de geradores, mas, quando precisa ficará feliz em ter isso na sua stack, Ele ajuda a abstrair interações e fornecem uma solução limpa para quando é necessário uma execução mais lenta dos valores solicitados.
Conclusão do Tradutor.
Esse artigo é uma tradução quase que direta do Artigo Original em inglês no medium.
Top comments (2)
Legal
Hmm interessante