Понимание генератора Javascript

У меня есть кусок кода:

function * input(){
    let array = [];
    while(true) {
        array.push(yield array);
    }
}

var gen = input();
console.log(gen.next("A"))
console.log(gen.next("B"))
console.log(gen.next("C"))
console.log(gen.next("D"))

Когда вы запустите его, вы получите следующий вывод:

{ value: [], done: false }
{ value: [ 'B' ], done: false }
{ value: [ 'B', 'C' ], done: false }
{ value: [ 'B', 'C', 'D' ], done: false }

Почему первая строка результата не включает A в массиве? На этой странице есть одно объяснение по адресу https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*. Комментарий гласит

первый вызов next() выполняется от начала функции до первого оператора yield

Но из моего тестирования это кажется не правильным. Мой тестовый код:

function* logGenerator() {
    console.log("before yield in function");
    yield 1;
    console.log("filler 1");
    yield 2;
    console.log("filler 2");
    yield 3;
    console.log("filler 3");
}

var gen = logGenerator();

console.log(gen.next());
console.log("-----------------");
console.log(gen.next());
console.log("-----------------");
console.log(gen.next());
console.log("-----------------");
console.log(gen.next());

Результат:

before yield in function
{ value: 1, done: false }
-----------------
filler 1
{ value: 2, done: false }
-----------------
filler 2
{ value: 3, done: false }
-----------------
filler 3
{ value: undefined, done: true }

Как видите, первый next() не только выполнил заявления перед первым yieldно и первый yield заявление. Так что эта теория не может объяснить мой вопрос. Может ли кто-нибудь помочь указать мне правильное направление? Заранее спасибо.

2 ответа

Рассмотрим первый генератор, который вы написали таким образом.

function * input(){
    let array = [];
    while(true) {
        var thingToAdd = yield array;
        console.log(thingToAdd);
        array.push(thingToAdd);
    }
}

var gen = input();
console.log(gen.next("A"))
console.log(gen.next("B"))
console.log(gen.next("C"))
console.log(gen.next("D"))

Разве не ясно, почему "А" никогда не добавляется в массив? Первое выполнение генератора останавливается на первом операторе yield задолго до того, как массив будет изменен. К тому времени, когда выполнение возвращается к генератору, переданное значение равно "B". Та же самая динамика происходит в вашем коде здесь array.push(yield array); Внутренние выражения оцениваются первыми, поэтому yield останавливает исполнение до push Доступ

Я считаю, что если вы хотите, чтобы генератор учитывал первое передаваемое вами значение, вам нужно вызывать.next() один раз без каких-либо параметров. Каждый пример, который я видел, делает это.

Также чтение раздела "Отправить" этой статьи иллюстрирует вашу ситуацию.

Обратите внимание, что эта модель хорошо работает для взаимодействия типа вопроса и ответа, поскольку у нас не может быть ответа до того, как задан вопрос, и все последующие next вызовы передадут ответ на предыдущий вопрос и вернут следующий.

var q1 = gen.next();
console.log(q1);
var a = userInput();
var q2 = gen.next(a);
console.log(q2);
var a2 = userInput();
...
function * foo () {
  var i = 0
  yield i

  i += 1
  yield i

  i += 1
  i += 2
  yield i
}

var gen = foo()
console.log(gen.next()) // 0
console.log(gen.next()) // 1
console.log(gen.next()) // 4

Обратите внимание, что var gen = foo() просто создает экземпляр генератора. Это первый вызов .next() который начинает выполнение генератора. Генераторы работают, пока не достигнут yield заявление и вернуть значение этого yield заявление. В этот момент генератор приостанавливается до следующего вызова .next() выполняется.

Итак, все работает так, как задумано в ваших примерах. В первом примере первый yield оператор возвращает пустой массив. На следующем .next() массив заполняется переданным значением, а затем этот массив получается. В коде:

function * foo (param) {
  var array = []
  // do nothing with param
  yield array

  array.push(param) // 'B'
  yield array

  array.push(param) // 'C'
  yield array

  array.push(param) // 'D'
  yield array
}

Это соответствует документации:

Если необязательное значение передается методу next() генератора, это значение становится значением, возвращаемым текущей операцией выхода генератора.

Другие вопросы по тегам