Repositório com intuito de exemplificar e descrever o funcionamento dos Iterators
e Generators
em Javascript
- Node 12 or +
Clone o repostório
git clone git@github.com:tiago154/iterators-e-generators.git
Instale as dependências
npm install
Um objeto é um iterator (iterador) quando sabe como acessar itens numa coleção, um por vez, enquanto mantém rastreada a posição atual em uma dada sequência. Em JavaScript um iterator é um objeto que oferece o método next(), o qual retorna o próximo item da sequência. Este método retorna um objeto com duas propriedades: done e value.
Execute o comando:
npm run 01
Um objeto é iterável (iterable), se ele define seu comportamento de iteração, como no caso de quais valores percorridos em um laço do tipo for..of. Alguns tipos embutidos, como o Array, ou o Map, têm um comportamento iterável padrão, enquanto outros tipos (como Object) não possuem.
Para que um objeto seja iterable, o objeto precisa implemntar o método @@iterator
, significando que o objeto (ou um dos objetos na cadeia de prototipagem - prototype chain) precisa ter uma propriedade com uma chave Symbol.iterator
:
Execute o comando:
npm run 02
Chamando um iterable implemetado pelo usuário com a função Next()
Execute o comando:
npm run 03
Alguns objetos no JavaScript por padrão já implementam esse protocolo, como por exemplo:
- String
- Array
- Map
- Set
- arguments (dentro de funções)
- NodeList (no browser)
Execute o comando:
npm run 04
Enquanto os iteradores são ferramentas muito úteis, sua criação requer um cuidado devido a necessidade de manter explícito seu estado interno. Generators provê uma alternativa poderosa: eles te permitem definir um algoritmo iterativo escrevendo uma função simples que pode manter seu estado próprio.
Generator é um tipo especial de função que trabalha como uma factory para iteradores. A função vira um generator se ela contém uma ou mais expressões yield
e se ela usa a sintaxe function*
.
Vale observar que não é possível definir um generator utilizando a sintaxe de arrow function, pois o JavaScript só reconhece que uma função define um generator através da declaração function*
Note que a execução das chamadas de yield
são lazy
, ou seja elas não são computadas todas de uma vez. A cada execução do iterador o estado da função permanece salvo e pode ser acessado nas execuções posteriores.
Execute o comando:
npm run 05
Execute o comando:
npm run 06
Execute o comando:
npm run 07
A expressão yield*
é usada para delegar para outro objeto generator
ou iterable
.
Execute o comando:
npm run 08
Generators não são indicados para melhorar o desempenho bruto de CPU, pois outras implementações se sairão melhores
Execute o comando:
npm run 09
Mas se voce necessita trabalhar com grandes listas e não impactar a memoria de sua aplicação, um generator pode ser uma opção a ser utilizada.
Antes de executar os exemplos a seguir, utilize o comando npm run generate-mock
e adicione a quantidade desejada de itens em mock na frente do comando.
Exemplo:
npm run generate-mock 1000000
No exemplo abaixo, o código irá manipular uma lista, realizar uma transformação de dados com o map e por fim salvar os dados. Podemos ver que o processamento é até rapido, mas o consumo de memória é bem alto.
Execute o comando:
npm run 10
Nesse outro exemplo, é feita a mesma coisa, mas é utilizado um generator, onde a manipulação de dados é feita em pedaços. Dessa forma reduzimos o consumo de memória e não teremos problemas caso a quantidade de elementos da lista aumente.
Execute o comando:
npm run 11