Incompatibility between generators and arrays in the Iterator protocol

# Tim Jansen (9 years ago)

I am working on a collections library that supports the Iterable/Iterator protocol, but I ran into a stumbling block: the iterator protocol seems to work differently for arrays and generator functions. Array iterators set 'done' to true after the last element, but function generators set 'done' to true on the last element.

Is this incompatibility intentional, or or just an implementation issue in the browsers (tested on FF38 and Chrome 43)? I wasn't able to find any information on this. The ES6 draft from April '15 doesn't define when exactly 'done' is set.

Code example:

var a = ['a', 'b'];
var itA = a[Symbol.iterator];

itA.next();   // {value: 'a', done: false}
itA.next();   // {value: 'b', done: false} !!
itA.next();   // {value: undefined, done: true}

function* b() {
  yield 'a';
  return 'b';
}

var itB = a[Symbol.iterator];
itB.next();   // {value: 'a', done: false}
itB.next();   // {value: 'b', done: true} !!!
itB.next();   // {value: undefined, done: true}

The difference is in the second invocation of next(), which returns true for generator functions. That protocol makes it impossible for me to support both generator functions and array iterators with the same implementation - at least if I want to support 'undefined' as a valid value in collections. I wouldn't be able to differentiate between an empty list ([]) and a list containing undefined ([undefined]).

# Kevin Smith (9 years ago)

If you use yield instead of return in your generator function you'll get the desired results. When using a generator function to implement iteration, you'll generally want to avoid returning a final value, since for-of ignores the return value.

The return value does have an important role to play in coroutine-style use cases.

# Logan Smyth (9 years ago)

The value set when done: true is set is not considered part of the iterated list, which is why you are seeing what you are seeing. The expected behavior is defined as done: false for all yielded values, and done: true when iteration has completed.

That same behavior also applies for for...of loops for instance. For example, see 13.7.5.13 #5c, as soon as 'done' becomes 'true' the loop exits, the returned value is ignored. Also the same can also be seen in Array.from in 22.1.2.1 #6.g.iv

# Ben Newman (9 years ago)

Kevin is right; I think the equivalence you're looking for is:

var arrayIter = [1, 2, ..., n][Symbol.iterator]();
var genIter = (function*() { yield 1; yield 2; ... yield n; })();
# Allen Wirfs-Brock (9 years ago)

On Jun 30, 2015, at 11:13 AM, Tim Jansen wrote:

Is this incompatibility intentional, or or just an implementation issue in the browsers (tested on FF38 and Chrome 43)? I wasn't able to find any information on this. The ES6 draft from April '15 doesn't define when exactly 'done' is set.

sure it does:

For example,

# Tim Jansen (9 years ago)

I see. I always considered the last value to be a part of the iteration, but if you don't (and for(of) treats it like that) it makes perfect sense.