diff --git a/1-js/12-generators-iterators/1-generators/article.md b/1-js/12-generators-iterators/1-generators/article.md index 9be227ad9..62cf92c0e 100644 --- a/1-js/12-generators-iterators/1-generators/article.md +++ b/1-js/12-generators-iterators/1-generators/article.md @@ -1,14 +1,14 @@ -# Generators +# Generatory -Regular functions return only one, single value (or nothing). +Zwykłe funkcje zwracają tylko jedną, pojedyńczą wartość (lub nie zwracają nic). -Generators can return ("yield") multiple values, one after another, on-demand. They work great with [iterables](info:iterable), allowing to create data streams with ease. +Generatory mogą zwrócić wiele wartości, być może ich nieskończoną ilość, jedna po drugiej, na żądanie. Świetnie działają z obiektami typu [iterowalnego](info:iterable), pozwalają na proste tworzenie strumieni danych. -## Generator functions +## Funkcje generujące -To create a generator, we need a special syntax construct: `function*`, so-called "generator function". +Aby stworzyć generator, używamy specjalnej składni: `function*`, jest to tak zwana "funkcja generująca". -It looks like this: +Wygląda to tak: ```js function* generateSequence() { @@ -18,9 +18,7 @@ function* generateSequence() { } ``` -Generator functions behave differently from regular ones. When such function is called, it doesn't run its code. Instead it returns a special object, called "generator object", to manage the execution. - -Here, take a look: +Funkcja `generateSequence()` nie wykonuje kodu, tylko zwraca specjalny obiekt, zwany "generatorem". ```js run function* generateSequence() { @@ -29,25 +27,25 @@ function* generateSequence() { return 3; } -// "generator function" creates "generator object" +// "funkcja generująca" tworzy "generator" let generator = generateSequence(); *!* alert(generator); // [object Generator] */!* ``` -The function code execution hasn't started yet: -![](generateSequence-1.svg) +Obiekt typu `generator` można rozumieć jako "zawieszone wywołanie funkcji". Po stworzeniu generatora, wykonanie kodu jest zapauzowane na samym początku: -The main method of a generator is `next()`. When called, it runs the execution until the nearest `yield ` statement (`value` can be omitted, then it's `undefined`). Then the function execution pauses, and the yielded `value` is returned to the outer code. +![](generateSequence-1.svg) -The result of `next()` is always an object with two properties: -- `value`: the yielded value. -- `done`: `true` if the function code has finished, otherwise `false`. +Główną metodą generatora jest `next()`. Wywołana, wznawia wykonanie aż do następnego polecenia typu `yield ` (`wartość` może być pominięta, ale będzie jako `undefined`). Wtedy wykonanie zostaje zatrzymane, a `wartość` zostaje zwrócona na zewnątrz. -For instance, here we create the generator and get its first yielded value: +Wynikiem `next()` jest zawsze obiekt o dwóch włąsciwościach: +- `value`: otrzymana wartość. +- `done`: `true` jeśli kod funkcji się zakończył wykonywać, w przeciwnym wypadku `false`. +Na przyklad, tu tworzymy generator i dostajemy jego pierwszą otrzymaną wartość. ```js run function* generateSequence() { yield 1; @@ -64,11 +62,12 @@ let one = generator.next(); alert(JSON.stringify(one)); // {value: 1, done: false} ``` -As of now, we got the first value only, and the function execution is on the second line: + +Jak na razie, dostajemy tylko pierwszą wartość, a wykonanie funkcji znajduje się w drugiej linii: ![](generateSequence-2.svg) -Let's call `generator.next()` again. It resumes the code execution and returns the next `yield`: +Wywołajmy `generator.next()` ponownie. Metoda wznawia wykonanie i zwraca następny `yield`: ```js let two = generator.next(); @@ -78,7 +77,7 @@ alert(JSON.stringify(two)); // {value: 2, done: false} ![](generateSequence-3.svg) -And, if we call it a third time, the execution reaches the `return` statement that finishes the function: +Jeśli wywołamy ją po raz trzeci, wykonanie zostanie wznowione aż do następnego polecenia `return`, które to kończy funkcje: ```js let three = generator.next(); @@ -88,21 +87,21 @@ alert(JSON.stringify(three)); // {value: 3, *!*done: true*/!*} ![](generateSequence-4.svg) -Now the generator is done. We should see it from `done:true` and process `value:3` as the final result. +Praca generatora dobiegła końca. Potwierdza to ostatni rezultat: `{ done: true, value: 3}`. -New calls to `generator.next()` don't make sense any more. If we do them, they return the same object: `{done: true}`. +Nowe wywołania `generator.next()` nie mają sensu. Jeśli je zrobimy, zwrócą ten sam obiekt: `{ done: true}`. -```smart header="`function* f(…)` or `function *f(…)`?" -Both syntaxes are correct. +```smart header="`function* f(…)` czy`function *f(…)`?". +Co kto lubi, obie składnie są poprawne. -But usually the first syntax is preferred, as the star `*` denotes that it's a generator function, it describes the kind, not the name, so it should stick with the `function` keyword. +Mimo to, w większości przypadków używamy pierwszej składni, ponieważ gwiazdka `*` wskazuje, że to funkcja generująca, tzn. opisuje rodzaj funkcji a nie jej nazwę, więc powinna być obok słowa `function`. ``` -## Generators are iterable +## Na generatorach można dokonywać iteracji. -As you probably already guessed looking at the `next()` method, generators are [iterable](info:iterable). +Jak wskazuje metoda `next()`, generatory to obiekty typu [iterowalnego](info: iterable) -We can get loop over values by `for..of`: +Możemy przechodzić po ich wartościach, za pomocą pętli `for..of`: ```js run function* generateSequence() { @@ -114,15 +113,15 @@ function* generateSequence() { let generator = generateSequence(); for(let value of generator) { - alert(value); // 1, then 2 + alert(value); // 1, następnie 2 } ``` -Looks a lot nicer than calling `.next().value`, right? +Jest to znacznie ładniejszy sposób na używanie generatorów, niż korzystanie z `.next().value`, prawda? -...But please note: the example above shows `1`, then `2`, and that's all. It doesn't show `3`! +...Weź proszę pod uwagę: przykład wyżej pokazuje `1`, następnie `2` i to wszystko. Nie pokazuje `3`! -It's because `for..of` iteration ignores the last `value`, when `done: true`. So, if we want all results to be shown by `for..of`, we must return them with `yield`: +Jest tak dlatego, ponieważ pętla `for..of` ignoruje ostatnią wartość `value`, jeżeli `done: true`. Jeśli chcemy pokazać wszystkie rezultaty za pomocą pętli `for..of`, to musimy je zwrócić za pomocą `yield`: ```js run function* generateSequence() { @@ -136,11 +135,11 @@ function* generateSequence() { let generator = generateSequence(); for(let value of generator) { - alert(value); // 1, then 2, then 3 + alert(value); // 1, następnie 2, następnie 3 } ``` -As generators are iterable, we can call all related functionality, e.g. the spread syntax `...`: +Oczywiście, generatory to obiekty typu iterowalnego, więc można też wywoływać na nich funkcjonalności powiązane z iteracją, np. składnie rozwinięcia `...`: ```js run function* generateSequence() { @@ -154,30 +153,30 @@ let sequence = [0, ...generateSequence()]; alert(sequence); // 0, 1, 2, 3 ``` -In the code above, `...generateSequence()` turns the iterable generator object into an array of items (read more about the spread syntax in the chapter [](info:rest-parameters-spread#spread-syntax)) +W kodzie powyżej, `...generateSequence()` zamienia iterowalny obiekt generatora w tablice wartości (przeczytaj więcej o składni rozwinięcia w rozdziale [parametr rest i składnia rozwinięcia](info:rest-parameters-spread-operator#spread-operator)) -## Using generators for iterables +## Używanie generatorów do iteracji -Some time ago, in the chapter [](info:iterable) we created an iterable `range` object that returns values `from..to`. +Jakiś czas temu w rozdziale [Iteracja](info:iterable) stworzyliśmy obiekt `range` typu iterowalnego, który zwraca wartości `od..do`. -Here, let's remember the code: +Dla przypomnienia, kod: ```js run let range = { from: 1, to: 5, - - // for..of range calls this method once in the very beginning + + //for...of range wywołuje tę metodę raz na samym początku [Symbol.iterator]() { - // ...it returns the iterator object: - // onward, for..of works only with that object, asking it for next values + // zwraca obiekt iteratora + // następnie, for..of działa tylko dla tego obiektu, pyta go o następną wartość return { current: this.from, last: this.to, - // next() is called on each iteration by the for..of loop + // next() jest wywoływana w każdej kolejnej iteracji przez pętle for..of next() { - // it should return the value as an object {done:.., value :...} + // to powinno zwrócić wartość jako obiekt {done:..., value:...} if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { @@ -188,20 +187,20 @@ let range = { } }; -// iteration over range returns numbers from range.from to range.to +// iteracja po obiekcie range zwraca liczby od range.from do range.to alert([...range]); // 1,2,3,4,5 ``` -We can use a generator function for iteration by providing it as `Symbol.iterator`. +Możemy użyć funkcji generatora do iteracji, podając ją jako `Symbol.iterator`: -Here's the same `range`, but much more compact: +Tutaj mamy ten sam obiekt `range`, ale znacznie bardziej elegancki: ```js run let range = { from: 1, to: 5, - *[Symbol.iterator]() { // a shorthand for [Symbol.iterator]: function*() + *[Symbol.iterator]() { // skrócona wersja zapisu: [Symbol.iterator]: function*() for(let value = this.from; value <= this.to; value++) { yield value; } @@ -211,25 +210,26 @@ let range = { alert( [...range] ); // 1,2,3,4,5 ``` -That works, because `range[Symbol.iterator]()` now returns a generator, and generator methods are exactly what `for..of` expects: -- it has a `.next()` method -- that returns values in the form `{value: ..., done: true/false}` +To działa, ponieważ `range[Symbol.iterator]` zwraca teraz generator, a metody generatora są dokładnie tym, czego oczekuje `for..of`: +- zawiera metodę `.next()` (tak jak generator) +- która zwraca wartości w formie `{ value: ..., done:true/false}` (tak samo jak generator). -That's not a coincidence, of course. Generators were added to JavaScript language with iterators in mind, to implement them easily. +Oczywiście to nie przypadek. Generatory zostały dodane do języka JavaScript z myślą o iteratorach, aby można je było łatwo zaimplementować. -The variant with a generator is much more concise than the original iterable code of `range`, and keeps the same functionality. +Wariant z generatorem jest znacznie bardziej zwięzły niż oryginalny kod `range` i zawiera te same funkcjonalności. -```smart header="Generators may generate values forever" -In the examples above we generated finite sequences, but we can also make a generator that yields values forever. For instance, an unending sequence of pseudo-random numbers. +```smart header="Generatory mogą działać bez końca" +W przykładach powyżej wygenerowaliśmy skończone ciągi, ale możemy też stworzyć generator, który zwraca wartości bez końca. Na przykład, niekończący się ciąg pseudo-losowych liczb. + +Wymagałoby to użycia `break` (lub `return`) w `for..of`, w przeciwnym wypadku pętla powtarzałaby sie bez końca. -That surely would require a `break` (or `return`) in `for..of` over such generator. Otherwise, the loop would repeat forever and hang. ``` -## Generator composition +## Kompozycja generatora -Generator composition is a special feature of generators that allows to transparently "embed" generators in each other. +Kompozycja generatora to specjalna cecha generatorów, która pozwala transparentnie "osadzić" generator w innym generatorze. -For instance, we have a function that generates a sequence of numbers: +Na przykład mamy funkcję, która generuje ciąg liczb: ```js function* generateSequence(start, end) { @@ -237,18 +237,18 @@ function* generateSequence(start, end) { } ``` -Now we'd like to reuse it to generate a more complex sequence: -- first, digits `0..9` (with character codes 48..57), -- followed by uppercase alphabet letters `A..Z` (character codes 65..90) -- followed by lowercase alphabet letters `a..z` (character codes 97..122) +Teraz chcielibyśmy użyć jej ponownie do wygenerowania bardziej złożonej sekwencji: +- zaczynająć od cyfr `0..9` (kody znaków 48..57) +- kontynuując po nich wielkich literach alfabetu `A..Z` (kody znaków 65..90) +- i kończąc na małych literach alfabetu `a..z` (kody znaków 97..122) -We can use this sequence e.g. to create passwords by selecting characters from it (could add syntax characters as well), but let's generate it first. +Następnie, mamy zamiar wytwarzać hasła przez wybieranie znaków z tego ciągu, ale najpierw musimy wygenerować ciąg. -In a regular function, to combine results from multiple other functions, we call them, store the results, and then join at the end. +W zwykłej funkcji, aby połączyć ze sobą rezultaty kilku różnych funkcji, wywołujemy je, przechowujemy rezultaty, a na końcu łączymy je ze sobą. -For generators, there's a special `yield*` syntax to "embed" (compose) one generator into another. +W przypadku generatorów istnieje specjalna składnia `yield*` do "osadzenia" (skomponowania) jednego generatora w drugim. -The composed generator: +Skomponowany generator: ```js run function* generateSequence(start, end) { @@ -279,9 +279,9 @@ for(let code of generatePasswordCodes()) { alert(str); // 0..9A..Za..z ``` -The `yield*` directive *delegates* the execution to another generator. This term means that `yield* gen` iterates over the generator `gen` and transparently forwards its yields outside. As if the values were yielded by the outer generator. +Dyrektywa `yield*` jest odpowiedzialna za kompozycję. *Deleguje* ona wykonanie do następnego generatora. Mówiąc prościej, dyrektywa uruchamia generatory i transparentnie przekazuje ich "yieldy" na zewnątrz, tak jakby zostały wykonane przez wywołany generator. -The result is the same as if we inlined the code from nested generators: +Rezultat jest taki sam, jak gdybyśmy wstawili kod w zagnieżdżone generatory: ```js run function* generateSequence(start, end) { @@ -312,22 +312,22 @@ for(let code of generateAlphaNum()) { alert(str); // 0..9A..Za..z ``` -A generator composition is a natural way to insert a flow of one generator into another. It doesn't use extra memory to store intermediate results. +Kompozycja generatora to naturalny sposób na przekierowanie strumienia jednego generatora w inny generator. Nie wymaga dodatkowej pamięci do przechowywania pośrednich rezultatów. -## "yield" is a two-way road +## "yield" to droga dwukierunkowa -Until this moment, generators were similar to iterable objects, with a special syntax to generate values. But in fact they are much more powerful and flexible. +Do tego momentu, traktowaliśmy generatory jako "iteratory na sterydach". I tak właśnie się z nich często korzysta. Ale tak naprawdę są one znacznie bardziej potężne i elastyczne. -That's because `yield` is a two-way road: it not only returns the result outside, but also can pass the value inside the generator. +To dlatego, ponieważ `yield` jest drogą dwukierunkową: nie tylko zwraca rezultaty na zewnątrz, ale może też przekazać wartość do środka generatora. -To do so, we should call `generator.next(arg)`, with an argument. That argument becomes the result of `yield`. +Aby to zrobić, powinniśmy wywołać `generator.next(arg)`, z argumentem. Ten argument jest rezultatem `yield`. -Let's see an example: +Oto przykład: ```js run function* gen() { *!* - // Pass a question to the outer code and wait for an answer + // Przekaż pytanie na zewnątrz i czekaj na odpowiedź. let result = yield "2 + 2 = ?"; // (*) */!* @@ -336,29 +336,27 @@ function* gen() { let generator = gen(); -let question = generator.next().value; // <-- yield returns the value +let question = generator.next().value; // <-- yield zwraca wartość -generator.next(4); // --> pass the result into the generator +generator.next(4); // --> przekazuje rezultat do generatora ``` ![](genYield2.svg) -1. The first call `generator.next()` should be always made without an argument (the argument is ignored if passed). It starts the execution and returns the result of the first `yield "2+2=?"`. At this point the generator pauses the execution, while staying on the line `(*)`. -2. Then, as shown at the picture above, the result of `yield` gets into the `question` variable in the calling code. -3. On `generator.next(4)`, the generator resumes, and `4` gets in as the result: `let result = 4`. - -Please note, the outer code does not have to immediately call`next(4)`. It may take time. That's not a problem: the generator will wait. +1. Pierwsze wywołanie `generator.next()` powinno być zawsze wykonane bez argumentu (argument jest ignorowany, jeśli zostanie przekazany). Rozpoczyna ono wykonanie i zwraca rezultat pierwszego `yield` ("2+2 = ?"). W tym momencie generator zatrzymuje wykonanie, pozostając na linii `(*)`. +2. Następnie, jak pokazane na powyższym obrazku, rezultat `yield` zostaje przypisany zmiennej `question` w wywoływanym kodzie. +3. Poprzez `generator.next(4)`, generator wznawia wykonanie, i `4` zostaje przypisane zmiennej result: `let result = 4`. -For instance: +Zauważ proszę, kod zewnętrzny nie musi od razu wywołać `next(4)`. Wyliczenie wartości może zająć troche czasu. To też jest prawidłowy kod: -```js -// resume the generator after some time +```js +// kontynuuj działanie generatora po pewnym czasie setTimeout(() => generator.next(4), 1000); ``` -As we can see, unlike regular functions, a generator and the calling code can exchange results by passing values in `next/yield`. +Jak widać, w przeciwieństwie do zwykłych funkcji, generator i kod wywołujący mogą wymieniać wyniki, przekazując wartości w `next/yield`. -To make things more obvious, here's another example, with more calls: +Aby uczynić sprawy bardziej oczywistymi, oto inny przykład, z większą ilością wywołań: ```js run function* gen() { @@ -380,36 +378,36 @@ alert( generator.next(4).value ); // "3 * 3 = ?" alert( generator.next(9).done ); // true ``` -The execution picture: +Przebieg wykonania: ![](genYield2-2.svg) -1. The first `.next()` starts the execution... It reaches the first `yield`. -2. The result is returned to the outer code. -3. The second `.next(4)` passes `4` back to the generator as the result of the first `yield`, and resumes the execution. -4. ...It reaches the second `yield`, that becomes the result of the generator call. -5. The third `next(9)` passes `9` into the generator as the result of the second `yield` and resumes the execution that reaches the end of the function, so `done: true`. +1. Pierwsze `.next()` rozpoczyna wykonanie... Dochodzi do pierwszego `yield`. +2. Rezultat zostaje przekazany do kodu zewnętrznego. +3. Drugi `.next(4)` przekazuje `4` z powrotem do generatora jako rezultat pierwszego `yield`, a następnie kontynuuje wykonanie. +4. ...Wykonanie dochodzi do drugiego `yield`, ten staje sie rezultatem wywołania generatora. +5. Trzeci `next(9)` przekazuje `9` do generatora, jako rezultat drugiego `yield`, następnie kontynuuje wykonywanie, które dochodzi do końca funkcji, a więc `done: true`. -It's like a "ping-pong" game. Each `next(value)` (excluding the first one) passes a value into the generator, that becomes the result of the current `yield`, and then gets back the result of the next `yield`. +To przypomina gre w "ping-pong". Każdy `next(value)` (za wyjątkiem pierwszego) przekazuje wartość do generatora, która staje sie rezultatem aktualnego `yield`, a następnie przekazuje rezultat następnego `yield`. ## generator.throw -As we observed in the examples above, the outer code may pass a value into the generator, as the result of `yield`. +Jak zauważyliśmy w przykładach wyżej, kod zewnętrzny może przekazać wartość do generatora, jako rezultat `yield`. -...But it can also initiate (throw) an error there. That's natural, as an error is a kind of result. +...Ale może też zainicjować (rzucić) błąd. Jest to naturalne, ponieważ błąd też jest rodzajem rezultatu. -To pass an error into a `yield`, we should call `generator.throw(err)`. In that case, the `err` is thrown in the line with that `yield`. +Aby przekazać błąd do `yield`, powinniśmy wywołać `generator.throw(err)`. W tym przypadku, `err` jest rzucony w linijce razem z danym `yield`. -For instance, here the yield of `"2 + 2 = ?"` leads to an error: +Na przykład, poniższy `yield "2 + 2 = ?"` prowadzi do błędu: ```js run function* gen() { try { let result = yield "2 + 2 = ?"; // (1) - alert("The execution does not reach here, because the exception is thrown above"); + alert("Wykonanie nie dosięga tej linii, z uwagi na wyjątek rzucony wyżej"); } catch(e) { - alert(e); // shows the error + alert(e); // pokazuje błąd } } @@ -418,19 +416,19 @@ let generator = gen(); let question = generator.next().value; *!* -generator.throw(new Error("The answer is not found in my database")); // (2) +generator.throw(new Error("Odpowiedź nie znaleziona w bazie danych.")); // (2) */!* ``` -The error, thrown into the generator at line `(2)` leads to an exception in line `(1)` with `yield`. In the example above, `try..catch` catches it and shows it. +Błąd, rzucony do generatora w linii `(2)` prowadzi do wyjątku w linii `(1)` z `yield`. W przykładzie powyżej, `try..catch` łapie błąd i pokazuje go. -If we don't catch it, then just like any exception, it "falls out" the generator into the calling code. +Jeśli nie złapiemy go, to tak samo, jak w przypadku każdego innego wyjątku, "wyrzuca" generator do kodu wywołującego. -The current line of the calling code is the line with `generator.throw`, labelled as `(2)`. So we can catch it here, like this: +Aktualna linia wywołującego kodu to linia z `generator.throw`, oznaczona jako `(2)`. Możemy więc złapać wyjątek, tak jak w przykładzie poniżej: ```js run function* generate() { - let result = yield "2 + 2 = ?"; // Error in this line + let result = yield "2 + 2 = ?"; // Błąd w tej linii. } let generator = generate(); @@ -439,23 +437,23 @@ let question = generator.next().value; *!* try { - generator.throw(new Error("The answer is not found in my database")); + generator.throw(new Error("Nie ma odpowiedzi w bazie danych")); } catch(e) { - alert(e); // shows the error + alert(e); // pokazuje błąd } */!* ``` -If we don't catch the error there, then, as usual, it falls through to the outer calling code (if any) and, if uncaught, kills the script. - -## Summary +Jeżeli nie złapiemy tam błędu, to tak jak zwykle, "spada" do zewnętrznego kodu, i jeżeli tam niezłapany, zabija nasz skrypt. -- Generators are created by generator functions `function* f(…) {…}`. -- Inside generators (only) there exists a `yield` operator. -- The outer code and the generator may exchange results via `next/yield` calls. +## Podsumowanie + + - Generatory są tworzone przez funkcje generujące `function* f(...) {...}`. + - Tylko w ciele generatorów może istnieć operator `yield`. + - Generator i kod zewnętrzny mogą wymieniać pomiędzy rezultaty, za pomocą wywołań `next/yield`. -In modern JavaScript, generators are rarely used. But sometimes they come in handy, because the ability of a function to exchange data with the calling code during the execution is quite unique. And, surely, they are great for making iterable objects. +We wspólczesnym języku JavaScript, generatory są rzadko używane. Czasami jednak mogą się przydać, ponieważ umiejętność wymiany danych pomiędzy funkcją a kodem zewnętrznym podczas wykonania jest dość unikalna. I z pewnością świetnie nadają się do tworzenia obiektów iterowalnych. -Also, in the next chapter we'll learn async generators, which are used to read streams of asynchronously generated data (e.g paginated fetches over a network) in `for await ... of` loops. +Poza tym, w następnym rozdziale, będziemy się uczyć o asynchronicznych generatorach, które są używane do czytania strumieni asynchronicznie wygenerowanych danych w pętlach `for await ... of`. -In web-programming we often work with streamed data, so that's another very important use case. +W programowaniu webowym, często pracujemy z danymi strumieniowymi, więc jest to kolejny bardzo ważny przypadek użycia. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy