You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/10-es-modern/12-generator/article.md
+23-18Lines changed: 23 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,9 +19,7 @@ function* generateSequence() {
19
19
}
20
20
```
21
21
22
-
При запуске `generateSequence()` код такой функции не выполняется!
23
-
24
-
Вместо этого она возвращает специальный объект, который как раз и называют "генератором".
22
+
При запуске `generateSequence()` код такой функции не выполняется. Вместо этого она возвращает специальный объект, который как раз и называют "генератором".
Функция завершена. Внешний код увидит это из свойства `done:true` и обработает `value:3`, как окончательный результат. Новые вызовы `generator.next()` больше не имеют смысла. Впрочем, если они и будут, то не вызовут ошибки, но будут возвращать один и тот же объект: `{done: true}`.
79
+
Функция завершена. Внешний код должен увидеть это из свойства `done:true` и обработать `value:3`, как окончательный результат.
80
+
81
+
Новые вызовы `generator.next()` больше не имеют смысла. Впрочем, если они и будут, то не вызовут ошибки, но будут возвращать один и тот же объект: `{done: true}`.
80
82
81
83
"Открутить назад" завершившийся генератор нельзя, но можно создать новый ещё одним вызовом `generateSequence()` и выполнить его.
82
84
83
85
[smart header="`function* (…)` или `function *(…)`?"]
84
-
Технически можно ставить звёздочку как сразу после `function`, так и позже, перед названием. В интернет можно найти обе эти формы записи, они верны:
86
+
Можно ставить звёздочку как сразу после `function`, так и позже, перед названием. В интернет можно найти обе эти формы записи, они верны:
85
87
```js
86
88
function*f() {
87
89
// звёздочка после function
@@ -92,15 +94,17 @@ function *f() {
92
94
}
93
95
```
94
96
95
-
Автор этого текста полагает, что правильнее использовать первый вариант `function*`, так как звёздочка относится к типу объявляемой сущности (`function*` -- "функция-генератор"), а не к её названию. Конечно, это всего лишь рекомендация-мнение, не обязательное к выполнению, работать будет и так и эдак.
97
+
Технически, нет разницы, но писать то так то эдак -- довольно странно, надо остановиться на чём-то одном.
98
+
99
+
Автор этого текста полагает, что правильнее использовать первый вариант `function*`, так как звёздочка относится к типу объявляемой сущности (`function*` -- "функция-генератор"), а не к её названию. Конечно, это всего лишь рекомендация-мнение, не обязательное к выполнению, работать будет в любом случае.
96
100
[/smart]
97
101
98
102
99
103
100
104
101
105
## Генератор -- итератор
102
106
103
-
Как вы, наверно, уже догадались по наличию метода `next()`, генератор является итерируемым объектом.
107
+
Как вы, наверно, уже догадались по наличию метода `next()`, генератор связан с [итераторами](/iterator). В частности, он является итерируемым объектом.
104
108
105
109
Его можно перебирать и через `for..of`:
106
110
@@ -261,7 +265,7 @@ for(let code of generateAlphaNum()) {
261
265
alert(str); // 0..9A..Za..z
262
266
```
263
267
264
-
Код выше по поведению полностью идентичен варианту с `yield*`. При этом, конечно, переменные вложенного генератора не попадают во внешний, "делегирование" только выводит результаты во внешний поток.
268
+
Код выше по поведению полностью идентичен варианту с `yield*`. При этом, конечно, переменные вложенного генератора не попадают во внешний, "делегирование" только выводит результаты `yield`во внешний поток.
265
269
266
270
Композиция -- это естественное встраивание одного генератора в поток другого. При композиции значения из вложенного генератора выдаются "по мере готовности". Поэтому она будет работать даже если поток данных из вложенного генератора оказался бесконечным или ожидает какого-либо условия для завершения.
Отдельно заметим вариант с `yield function(callback)`. Такие функции (с единственным-аргументом callback'ом) называют "thunk".
626
+
Отдельно заметим вариант с `yield function(callback)`. Такие функции, с единственным-аргументом callback'ом, в англоязычной литературе называют "thunk".
621
627
622
628
Функция обязана выполниться и вызвать (асинхронно) либо `callback(err)` с ошибкой, либо `callback(null, result)` с результатом.
623
629
624
-
Этот способ использования является устаревшим, так как там, где можно использовать `yield function(callback)`, можно использовать и промисы. При этом промисы мощнее. Но в старом коде его ещё можно встретить.
630
+
Использование таких функций в `yield`является устаревшим подходом, так как там, где можно использовать `yield function(callback)`, можно использовать и промисы. При этом промисы мощнее. Но в старом коде его ещё можно встретить.
625
631
[/smart]
626
632
627
633
@@ -641,7 +647,7 @@ function* gen() {
641
647
}
642
648
643
649
function*gen2() {
644
-
let result =yieldnewPromise(
650
+
let result =yieldnewPromise(// (1)
645
651
resolve=>setTimeout(resolve, 1000, 'hello')
646
652
);
647
653
return result;
@@ -650,7 +656,7 @@ function* gen2() {
650
656
651
657
Это -- отличный вариант для библиотеки `co`. Композиция `yield* gen()` вызывает `gen()` в потоке внешнего генератора. Аналогично делает и `yield* gen()`.
652
658
653
-
Поэтому `yield new Promise` из `gen2()` попадает напрямую в библиотеку `co`, как еслы бы он был сделан так:
659
+
Поэтому `yield new Promise` из строки `(1)` в `gen2()` попадает напрямую в библиотеку `co`, как если бы он был сделан так:
654
660
655
661
```js
656
662
//+ run
@@ -750,14 +756,13 @@ let user = yield fetchUser(url);
750
756
// let user = yield* fetchUser(url);
751
757
```
752
758
753
-
То есть, можно сделать `yield` генератора, `co()` его выполнит и передаст значение обратно. Как мы видели выше, библиотека `co` -- довольно всеядна, поэтому будет работать и так и эдак.
754
-
Однако, рекомендуется использовать для вызова функций-генераторов именно `yield*`.
759
+
То есть, можно сделать `yield` генератора, `co()` его выполнит и передаст значение обратно. Как мы видели выше, библиотека `co` -- довольно всеядна. Однако, рекомендуется использовать для вызова функций-генераторов именно `yield*`.
755
760
756
761
Причин для этого несколько:
757
762
<ol>
758
763
<li>Делегирование генераторов `yield*` -- это встроенный механизм JavaScript. Вместо возвращения значения обратно в `co`, выполнения кода библиотеки... Мы просто используем возможности языка. Это правильнее.</li>
759
764
<li>Поскольку не происходит лишних вызовов, это быстрее по производительности.</li>
760
-
<li>И, наконец, пожалуй, самое приятное -- делегирование генераторов не портит стек.</li>
765
+
<li>И, наконец, пожалуй, самое приятное -- делегирование генераторов сохраняет стек.</li>
0 commit comments