HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>В Computer Science под генераторами понимается data producer, то есть сущность в языке, которая только выдает наружу данные, используя yield. При этом существует более общая концепция, которая называется coroutine или сопрограмма. В отличие от генераторов, она может не только генерировать данные, но так же может и потреблять их (data consumer). Самым удивительным в этой истории является то, что генераторы в js, по сути, являются корутинами, а использование их в качестве генераторов - это всего лишь один из возможных вариантов.</p>
1 <p>В Computer Science под генераторами понимается data producer, то есть сущность в языке, которая только выдает наружу данные, используя yield. При этом существует более общая концепция, которая называется coroutine или сопрограмма. В отличие от генераторов, она может не только генерировать данные, но так же может и потреблять их (data consumer). Самым удивительным в этой истории является то, что генераторы в js, по сути, являются корутинами, а использование их в качестве генераторов - это всего лишь один из возможных вариантов.</p>
2 <blockquote><p>Сопрограмма - компонент программы, обобщающий понятие функции, который дополнительно поддерживает множество входных точек (а не одну, как функция), остановку и продолжение выполнения с сохранением определенного положения.</p>
2 <blockquote><p>Сопрограмма - компонент программы, обобщающий понятие функции, который дополнительно поддерживает множество входных точек (а не одну, как функция), остановку и продолжение выполнения с сохранением определенного положения.</p>
3 </blockquote><p>Главное, на что нужно обратить внимание, это появление выражения yield справа от знака равно: const a = yield 10.</p>
3 </blockquote><p>Главное, на что нужно обратить внимание, это появление выражения yield справа от знака равно: const a = yield 10.</p>
4 <p>Попробуем по шагам выполнить этот код:</p>
4 <p>Попробуем по шагам выполнить этот код:</p>
5 <ol><li>Создание корутины const coroutine = gen();</li>
5 <ol><li>Создание корутины const coroutine = gen();</li>
6 <li>Вызов next(). Первый вызов приводит к тому, что наружу возвращается { value: 10, done: false }, так как внутри мы оказываемся в точке yield 10.</li>
6 <li>Вызов next(). Первый вызов приводит к тому, что наружу возвращается { value: 10, done: false }, так как внутри мы оказываемся в точке yield 10.</li>
7 <li>Вызов next(result.value + 1). Выражение result.value + 1 равно 11, поэтому в итоге происходит вызов next(11). Внутри корутины мы находимся в этой позиции const a = yield. Аргумент, переданный в next, оказывается записанным в константу a внутри корутины и код продолжает выполняться до следующего вызова yield, на котором корутина останавливается, и управление возвращается наружу.</li>
7 <li>Вызов next(result.value + 1). Выражение result.value + 1 равно 11, поэтому в итоге происходит вызов next(11). Внутри корутины мы находимся в этой позиции const a = yield. Аргумент, переданный в next, оказывается записанным в константу a внутри корутины и код продолжает выполняться до следующего вызова yield, на котором корутина останавливается, и управление возвращается наружу.</li>
8 <li>Дальнейший вызов next(15) приводит к тому, что константа b становится равна 15, а наружу возвращается { value: 26, done: true }.</li>
8 <li>Дальнейший вызов next(15) приводит к тому, что константа b становится равна 15, а наружу возвращается { value: 26, done: true }.</li>
9 </ol><p>Если обобщить, то yield &lt;что-то&gt; производит данные наружу, const a = yield потребляет данные, а const a = yield &lt;что-то&gt; производит и потребляет в два шага.</p>
9 </ol><p>Если обобщить, то yield &lt;что-то&gt; производит данные наружу, const a = yield потребляет данные, а const a = yield &lt;что-то&gt; производит и потребляет в два шага.</p>
10 <p>Теперь, используя немного магии, мы можем создать обертку над генераторами для работы с асинхронным кодом. Функция co будет такой оберткой. Ниже пример как она используется:</p>
10 <p>Теперь, используя немного магии, мы можем создать обертку над генераторами для работы с асинхронным кодом. Функция co будет такой оберткой. Ниже пример как она используется:</p>
11 <p>Идея в том, что функция co автоматически итерирует по генератору, извлекая значение из промисов и передавая их дальше в next по цепочке. В целом, на этом можно было бы и остановиться, но для полной имитации синхронной работы хотелось бы поддержки со стороны try/catch. И генераторы дают возможность трансформировать ошибки в исключения.</p>
11 <p>Идея в том, что функция co автоматически итерирует по генератору, извлекая значение из промисов и передавая их дальше в next по цепочке. В целом, на этом можно было бы и остановиться, но для полной имитации синхронной работы хотелось бы поддержки со стороны try/catch. И генераторы дают возможность трансформировать ошибки в исключения.</p>
12 <p>Чтобы такой код заработал, необходимо в функции co отслеживать состояние rejected и использовать метод throw, который есть у нашего генератора. Ниже пример того, как это можно было бы сделать (без промисов):</p>
12 <p>Чтобы такой код заработал, необходимо в функции co отслеживать состояние rejected и использовать метод throw, который есть у нашего генератора. Ниже пример того, как это можно было бы сделать (без промисов):</p>
13 <p>Метод throw() возобновляет выполнение тела генератора, кидая внутри исключение, и возвращает объект со свойствами done и value.</p>
13 <p>Метод throw() возобновляет выполнение тела генератора, кидая внутри исключение, и возвращает объект со свойствами done и value.</p>