Страницы

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

среда, 4 декабря 2019 г.

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

#javascript #ecmascript_2015


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

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.


Хотелось бы узнать, в чём именно заключается разница и в каких случаях она проявит себя.
    


Ответы

Ответ 1



На самом деле, синтаксис 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!" Ситуация, с пробросом исключений в дочерний итератор, описанная участником @Роман Парадеев в соседнем ответе, является всего лишь частным случаем передачи управления дочернему генератору.

Ответ 2



Вот что говорит на этот счёт говорит ECMAScript Wiki: This is similar to a for-in loop over the generator, except that it propagates exceptions thrown via the outer generator’s throw method into the delegated generator. Так что разница, похоже, только в пробросе исключений. Я набросал небольшой пример, который иллюстрирует различие: function * delegate() { try { yield; } catch (error) { console.log('Caught by delegate'); } } function * gen1() { console.log('Generator 1'); try { yield * delegate(); } catch (error) { console.log('Caught by generator'); } } function * gen2() { console.log('Generator 2'); try { for (let x of delegate()) { yield x; } } catch (error) { console.log('Caught by generator'); } } [gen1, gen2].forEach(gen => { const genObj = gen(); genObj.next(); genObj.throw('Uh oh...'); }); Как и ожидалось, вывод будет следующий Generator 1 Caught by delegate Generator 2 Caught by generator

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

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