Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Iteratory i Generatory

W laboratorium Parku Jurajskiego naukowcy analizują sekwencje DNA dinozaurów - element po elemencie, nigdy nie ładując całego genomu do pamięci. Iteratory i generatory w JavaScript działają podobnie - pozwalają przetwarzać dane element po elemencie, leniwie i efektywnie.

Protokół iterowalności

Każdy obiekt, który implementuje metodę

[Symbol.iterator]()
, jest iterowalny i może być użyty w pętli
for...of
.

1// Tablice, stringi, Map, Set - są iterowalne z natury
2for (const char of 'ATCG') {
3  console.log(char); // A, T, C, G
4}
5
6for (const item of [1, 2, 3]) {
7  console.log(item); // 1, 2, 3
8}

Tworzenie własnego iteratora

1const dinoEnclosure = {
2  dinosaurs: ['Rex', 'Blue', 'Delta', 'Echo'],
3
4  [Symbol.iterator]() {
5    let index = 0;
6    const dinos = this.dinosaurs;
7
8    return {
9      next() {
10        if (index < dinos.length) {
11          return { value: dinos[index++], done: false };
12        }
13        return { value: undefined, done: true };
14      }
15    };
16  }
17};
18
19// Teraz możemy użyć for...of!
20for (const dino of dinoEnclosure) {
21  console.log('Dinozaur:', dino);
22}
23
24// Oraz spread operator
25const allDinos = [...dinoEnclosure];
26console.log(allDinos); // ['Rex', 'Blue', 'Delta', 'Echo']

for...of vs for...in

1const park = { name: 'Jurassic', size: 'large', open: false };
2const species = ['T-Rex', 'Velociraptor', 'Triceratops'];
3
4// for...in - iteruje po KLUCZACH (obiektów i tablic)
5for (const key in park) {
6  console.log(key); // 'name', 'size', 'open'
7}
8
9for (const index in species) {
10  console.log(index); // '0', '1', '2' (stringi!)
11}
12
13// for...of - iteruje po WARTOŚCIACH (iterowalnych)
14for (const animal of species) {
15  console.log(animal); // 'T-Rex', 'Velociraptor', 'Triceratops'
16}
17
18// for...of NIE działa na zwykłych obiektach!
19// for (const val of park) {} // TypeError!
20// Ale działa na Object.entries():
21for (const [key, val] of Object.entries(park)) {
22  console.log(key, val);
23}

Generatory (function*)

Generatory to specjalne funkcje, które mogą pauzować swoje wykonanie i wznawiać je później.

1function* dinoGenerator() {
2  console.log('Generuję pierwszego dinozaura...');
3  yield 'T-Rex';
4
5  console.log('Generuję drugiego...');
6  yield 'Velociraptor';
7
8  console.log('Generuję trzeciego...');
9  yield 'Triceratops';
10
11  console.log('Koniec generacji!');
12}
13
14const gen = dinoGenerator();
15
16console.log(gen.next()); // { value: 'T-Rex', done: false }
17console.log(gen.next()); // { value: 'Velociraptor', done: false }
18console.log(gen.next()); // { value: 'Triceratops', done: false }
19console.log(gen.next()); // { value: undefined, done: true }

Generator nieskończony

1function* idGenerator(prefix) {
2  let id = 1;
3  while (true) {
4    yield prefix + '-' + String(id).padStart(3, '0');
5    id++;
6  }
7}
8
9const dinoIds = idGenerator('DINO');
10console.log(dinoIds.next().value); // 'DINO-001'
11console.log(dinoIds.next().value); // 'DINO-002'
12console.log(dinoIds.next().value); // 'DINO-003'
13// Nigdy się nie kończy - generuje na żądanie!

yield* - delegacja do innego generatora

1function* carnivores() {
2  yield 'T-Rex';
3  yield 'Velociraptor';
4}
5
6function* herbivores() {
7  yield 'Triceratops';
8  yield 'Brachiosaurus';
9}
10
11function* allDinosaurs() {
12  yield* carnivores();
13  yield* herbivores();
14}
15
16for (const dino of allDinosaurs()) {
17  console.log(dino);
18}
19// T-Rex, Velociraptor, Triceratops, Brachiosaurus

Async Generatory

1async function* fetchDinoPages(apiUrl) {
2  let page = 1;
3  let hasMore = true;
4
5  while (hasMore) {
6    const response = await fetch(apiUrl + '?page=' + page);
7    const data = await response.json();
8
9    yield data.results;
10
11    hasMore = data.hasNextPage;
12    page++;
13  }
14}
15
16// Użycie z for await...of
17async function loadAllDinos() {
18  for await (const batch of fetchDinoPages('/api/dinosaurs')) {
19    console.log('Załadowano grupę:', batch.length, 'dinozaurów');
20  }
21}

Praktyczne zastosowania

1// 1. Lazy evaluation - przetwarzanie na żądanie
2function* fibonacci() {
3  let a = 0, b = 1;
4  while (true) {
5    yield a;
6    [a, b] = [b, a + b];
7  }
8}
9
10// Weź tylko 10 pierwszych
11const fib = fibonacci();
12const first10 = [];
13for (let i = 0; i < 10; i++) {
14  first10.push(fib.next().value);
15}
16console.log(first10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
17
18// 2. Paginacja - ładuj strony na żądanie
19function* paginate(items, pageSize) {
20  for (let i = 0; i < items.length; i += pageSize) {
21    yield items.slice(i, i + pageSize);
22  }
23}
24
25const allSpecies = ['A','B','C','D','E','F','G','H'];
26const pages = paginate(allSpecies, 3);
27console.log(pages.next().value); // ['A', 'B', 'C']
28console.log(pages.next().value); // ['D', 'E', 'F']
29console.log(pages.next().value); // ['G', 'H']

"Iteratory i generatory to jak taśma produkcyjna w laboratorium" - mówi naukowiec. "Nie musisz przetwarzać wszystkiego naraz - dostajesz elementy dokładnie wtedy, gdy ich potrzebujesz!"

Vai a CodeWorlds