This post is cross posted in Medium, here.
I recently found one interesting thing about ES6 Generators. I come from Python background and I understood generators as in Python. So, I expected the following Python code's equivalent ECMAScript 6 code also to work as well.
But then, when I used Babel to transpile the following code and executed it
I recently found one interesting thing about ES6 Generators. I come from Python background and I understood generators as in Python. So, I expected the following Python code's equivalent ECMAScript 6 code also to work as well.
>>> numbers = (num for num in range(10))You can find the online demo for this Python program, here.
>>> for num in numbers:
... if num == 3:
... break
...
>>> next(numbers)
4
But then, when I used Babel to transpile the following code and executed it
function* NumberGenerator() {You can find the online demo for this JavaScript program, made with Babel's REPL, here. As you see here, when I broke out of the loop, the Generator Object got closed. This was pointed out to me by Logan Smyth in Babel's Slack discussion. I was really surprised by this behavior and found the 13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation ( lhs, stmt, iterator, lhsKind, labelSet ) section in the ECMAScript 6 Specification, which says
for (var i = 0; i < 10; i += 1) {
yield i;
}
}
var numbers = NumberGenerator();
for (var num of numbers) {
if (num === 3) {
break;
}
}
console.log(numbers.next());
// {"done":true}
If LoopContinues(result, labelSet) is false, return IteratorClose(iterator, UpdateEmpty(result, V)).I am not sure about the rationale behind that decision, but I am convinced that it would effectively limit the potential of the Generators. So I decided to fix this.
Sane Generators
To close the iterator,Iterator.prototype.return
is called. (At the time of this writing, not many JavaScript Engines support this function. You can find the support for this feature by popular engines, here.) So I decided to override that and allow the actual return
function to be invoked only when explicitly called with an argument.function returnFunction(originalReturn, genObject) {You can see the actual and complete implementation in my GitHub repository, https://github.com/thefourtheye/sane-generator. Now, you can use the
return function(arg) {
return arguments.length ? originalReturn.call(genObject, arg) : {
done: false
};
};
}
function SaneGenerator(genObject) {
var originalReturn = genObject['return'];
if (typeof originalReturn === 'function') {
Object.defineProperty(genObject, 'return', {
value: returnFunction(originalReturn, genObject)
});
}
return genObject;
}
SaneGenerator
like this function* NumberGenerator() {You can find the online demo for this JavaScript program, made with Babel's REPL, here.
for (var i = 0; i < 10; i += 1) {
yield i;
}
}
var numbers = SaneGenerator(NumberGenerator());
for (var num of numbers) {
if (num === 3) {
break;
}
}
console.log(numbers.next());
// {"value":4,"done":false}