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?
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. abreak
statement is encountered or an error is thrown), thereturn()
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.