Страницы

Поиск по вопросам

понедельник, 8 октября 2018 г.

Что скрывает yield *?

В чём разница между этими фрагментами кода?
yield *smth;

for (let x of smth) { yield x; }
@torazaburo в комментарии сказал
This is an OK simplification, but it fails to address what the yield *generator() evaluates to, which as it turns out is the end-of-iteration value return'ed by the generator object. It also does not correctly represent what happens when the consumer of the generator throws into the generator, etc.
Хотелось бы узнать, в чём именно заключается разница и в каких случаях она проявит себя.


Ответ

На самом деле, синтаксис yield * gen используется для передачи управления внутрь другого генератора.
Вот что об этом говорит MDN
The yield* expression iterates over the operand and yields each value returned by it.
А вот и пример того, как это работает:
let seqPos = function * (max) { for (let i = 1; i <= max; i++) { yield i; } }
let seqNeg = function * (min) { let start = min > 0 ? -1*min : min; for (let i = start; i < 0; i++) { yield i; } }
let seq = function * (val) { yield * seqNeg(-1*val); yield 0; yield * seqPos(val); }
for (let i of seq(2)) { console.log(i); }
Пример выше выведет:
-2 -1 0 1 2
А вот и JSFiddle с примером.

Что касается основного вопроса, то разница между конструкциями:
for (let val of gen) { yield val; }
и
yield * gen;
заключается в том, что в первом случае всего лишь возвращаются значения дочернего генератора, а во втором управление полностью передается дочернему генератору.
Это значит, что вызов методов Generator.prototype.next, Generator.prototype.throw и Generator.prototype.return клиентским кодом на родительском генераторе будет адресован дочернему генератору:
let innerGen = function * () { try { for (let i = 0; i < 3; i++) { console.log(yield i); } } catch (e) { console.log('Got you!'); } }
let outerGen = function * () { let child = innerGen(); yield * child; }
let g = outerGen(); g.next(); g.next("foo"); // выведет в консоль "foo" g.throw(new Error('Oops!')); // выведет "Got you!"
Ситуация, с пробросом исключений в дочерний итератор, описанная участником @Роман Парадеев в соседнем ответе, является всего лишь частным случаем передачи управления дочернему генератору.

Комментариев нет:

Отправить комментарий