javascriptgenerator

Partial iteration of a generator using for ... of


Here is a test function, which gets a first item from a sequence using for ... of loop. And after that it gets a second item:

function test(iterable) {
  const iterator = iterable[Symbol.iterator]();
  for (const item of iterator) {
    console.log('First: ' + item);
    break;
  }
  for (const item of iterator) {
    console.log('Second: ' + item);
    break;
  }
}

function test2(iterable) {
  const iterator = iterable[Symbol.iterator]();
  const item = iterator.next();
  if (!item.done) {
    console.log('First: ' + item.value);
  }
  for (const item of iterator) {
    console.log('Second: ' + item);
    break;
  }
}

const obj = {
  *[Symbol.iterator]() {
    yield* [1, 2, 3, 4, 5];
  }
}

console.log('test:');
test([1, 2, 3, 4, 5]);
test(obj);

console.log('\ntest2:');
test2([1, 2, 3, 4, 5]);
test2(obj);

It works fine for an array. But it doesn't work for a generator. In the later case it doesn't get the second item.

However test2 function works fine in both cases.

Is it an expected behaviour or a bug?


Solution

  • When for..of loop is exited -- either normally or via break, the iterator's return method is executed (if it is defined), which indicates to the iterator that it should close. By consequence, if subsequent code then tries to consume more values from that same iterator, it will find that there are no more values to consume.

    The obj generator function that you have defined will return an iterator that has this return method. The array iterator does not implement this method. This explains the difference in behaviour.

    Quoting Mozilla Contributors from the article on [for...of]:

    If the for...of loop exited early (e.g. a break statement is encountered or an error is thrown), the return() method of the iterator is called to perform any cleanup.

    And:

    Generators implement the return() method, which causes the generator function to early return when the loop exits. This makes generators not reusable between loops.