Funkcje generatorów w JavaScript

Funkcje generatorów w JavaScript

W tym artykule wyjaśnimy funkcje generatorów w JavaScript.

YouTube Video

Funkcje generatorów w JavaScript

Funkcje generatorów w JavaScript to specjalne funkcje, które różnią się od zwykłych funkcji tym, że mogą przeprowadzić leniwe wykonanie oraz zatrzymywanie i wznawianie działania. Rozumiejąc funkcje generatorów, możesz efektywnie zarządzać operacjami asynchronicznymi i sekwencyjnym przetwarzaniem dużych zbiorów danych. Tutaj szczegółowo wyjaśnimy, jak działają funkcje generatorów, jak z nich korzystać oraz przedstawimy przykłady praktycznego zastosowania.

Czym są funkcje generatorów?

Funkcja generatora jest definiowana za pomocą function* (definicji funkcji z gwiazdką) i, w przeciwieństwie do zwykłych funkcji, może zatrzymać wykonywanie w połowie i wznowić je, zachowując swój stan. Funkcje generatorów umożliwiają „leniwe wykonanie” oraz zwracanie wyników w sposób przyrostowy, co pozwala na efektywne zarządzanie pamięcią i przetwarzanie sekwencyjne.

Składnia

1function* generatorFunction() {
2    yield 'First value';
3    yield 'Second value';
4    return 'Done';
5}

W ten sposób funkcja generatora może mieć wiele wyrażeń yield i używać yield do zatrzymywania swojego działania. Kiedy funkcja generatora jest wywoływana, jej ciało nie jest wykonywane natychmiast, a zwracany jest obiekt generatora. Możesz wywołać metodę next() na tym obiekcie, aby wznowić funkcję od miejsca, gdzie została zatrzymana.

Podstawowe zastosowanie funkcji generatora

Teraz przyjrzyjmy się podstawowemu przykładowi użycia funkcji generatorów.

 1function* simpleGenerator() {
 2    yield 1;
 3    yield 2;
 4    yield 3;
 5}
 6
 7const gen = simpleGenerator();
 8
 9console.log(gen.next()); // { value: 1, done: false }
10console.log(gen.next()); // { value: 2, done: false }
11console.log(gen.next()); // { value: 3, done: false }
12console.log(gen.next()); // { value: undefined, done: true }

Kluczową rzeczą do odnotowania jest to, że generator zwraca wartości za każdym razem za pomocą yield, a dopóki właściwość done ma wartość false, oznacza to, że wciąż są kolejne wartości do zwrócenia. Ostatnie wywołanie next() zwraca done: true, co oznacza, że generator zakończył działanie.

Słowo kluczowe yield i zatrzymywanie wartości

yield to słowo kluczowe, które wskazuje punkt zatrzymania w funkcji generatora. Wartość po prawej stronie yield jest zwracana, gdy wywoływane jest next(). Dodatkowo, yield umożliwia komunikację dwukierunkową. Innymi słowy, gdy przekazujesz wartość jako argument do metody next(), wartość ta jest przesyłana do funkcji generatora.

 1function* generatorWithYield() {
 2    const value1 = yield 'First yield';
 3    console.log('Received value:', value1);
 4    const value2 = yield 'Second yield';
 5    console.log('Received value:', value2);
 6}
 7
 8const gen = generatorWithYield();
 9
10console.log(gen.next());        // { value: 'First yield', done: false }
11console.log(gen.next('Apple')); // Received value: Apple
12                                // { value: 'Second yield', done: false }
13console.log(gen.next('Banana'));// Received value: Banana
14                                // { value: undefined, done: true }

W tym przykładzie wywołanie next('Apple') przesyła wartość 'Apple' do funkcji generatora, gdzie jest używana wewnątrz funkcji.

Zarządzanie stanem generatora

Generatory mogą utrzymywać stan wykonywania, co pozwala na zwięzłą reprezentację długich pętli lub sekwencyjnego przetwarzania. Poniższy przykład przedstawia generator, który generuje liczby w nieskończoność.

 1function* infiniteGenerator() {
 2    let i = 0;
 3    while (true) {
 4        yield i++;
 5        if (i > 10) {
 6            break;
 7        }
 8    }
 9}
10
11const gen = infiniteGenerator();
12
13console.log(gen.next().value); // 0
14console.log(gen.next().value); // 1
15console.log(gen.next().value); // 2
16// Continues...

Ten generator nieustannie generuje liczby za pomocą pętli while(true), umożliwiając pobieranie wartości w razie potrzeby. To pozwala na efektywne przetwarzanie dużych zbiorów danych.

Zastosowania funkcji generujących

Funkcje generujące nadają się do wykonywania wielu procesów sekwencyjnie. Na przykład są przydatne w przetwarzaniu żądań API sekwencyjnie lub w dzieleniu i przetwarzaniu dużych plików.

1function* apiRequestGenerator() {
2    yield fetch('https://codesparklab.com/json/example1.json');
3    yield fetch('https://codesparklab.com/json/example2.json');
4    yield fetch('https://codesparklab.com/json/example3.json');
5}

Generatory, które mogą być używane do przetwarzania asynchronicznego, są niezwykle korzystne dla efektywnego sekwencyjnego zarządzania danymi.

Generatory asynchroniczne

Generatory asynchroniczne wprowadzone w ES2018 pozwalają na zwracanie wartości asynchronicznych sekwencyjnie poprzez połączenie async i yield. To umożliwia zwięzłe pisanie przetwarzania asynchronicznego w połączeniu z await.

Składnia

 1async function* asyncGenerator() {
 2    yield await Promise.resolve(1);
 3    yield await Promise.resolve(2);
 4    yield await Promise.resolve(3);
 5}
 6
 7const gen = asyncGenerator();
 8
 9(async () => {
10    for await (const value of gen) {
11        console.log(value); // 1, 2, 3
12    }
13})();

Generatory asynchroniczne mogą pobierać wartości sekwencyjnie za pomocą pętli for await...of. Ten wzorzec jest szczególnie przydatny podczas obsługi strumieni danych asynchronicznych.

Praktyczny przykład: upraszczanie przetwarzania asynchronicznego za pomocą generatorów

Funkcje generujące są również używane do uproszczenia przepływu przetwarzania asynchronicznego. Na przykład, łącząc yield i Promise, jak pokazano poniżej, możesz napisać operacje asynchroniczne, które wyglądają jak synchroniczne.

 1function* asyncFlow() {
 2    const data1 = yield fetch('https://codesparklab.com/json/example1.json');
 3    console.log(data1);
 4    const data2 = yield fetch('https://codesparklab.com/json/example2.json');
 5    console.log(data2);
 6}
 7
 8const gen = asyncFlow();
 9
10function handleAsync(generator) {
11    const next = (promise) => {
12        promise.then((result) => {
13        const { value, done } = generator.next(result);
14        if (!done) {
15            next(value);
16        }
17        });
18    };
19
20    next(generator.next().value);
21}
22
23handleAsync(gen);

Ten kod sekwencyjnie wykonuje żądania API za pomocą generatora i przetwarza wyniki.

Podsumowanie

Funkcje generujące są jedną z potężnych funkcji JavaScriptu, posiadają zdolność wstrzymywania i wznawiania wykonywania funkcji. To pozwala na przetwarzanie sekwencyjne, przetwarzanie asynchroniczne oraz efektywną manipulację dużymi zbiorami danych. Zrozumienie generatorów jest ważnym krokiem w opanowaniu zaawansowanych technik w JavaScripcie.

Możesz śledzić ten artykuł, korzystając z Visual Studio Code na naszym kanale YouTube. Proszę również sprawdzić nasz kanał YouTube.

YouTube Video