Because closures are less convenient for simple iteration; simplifying syntax for reasonably common tasks can be worth it, even if the language supported the same pattern before. Just compare:
function chain() {
var args = Array.from(arguments);
return function() {
if (args.length === 0) return undefined; // Or some other sentinel
var nextval = args[0].shift(); // Destructive to avoid copies or more closure vars
if (args[0].length === 0) args.shift();
return nextval;
};
}
var x;
// a, b and c must be indexable, e.g. Arrays; we can't handle other closures without
// requiring some API specific protocol for generation
for (var nextchain = chain(a, b, c); (x = nextchain()) !== undefined;) {
// do stuff with current value
}
to:
function* chain() {
for (var i = 0; i < arguments.length; ++i)
yield* arguments[i];
}
// a, b and c can be any iterable object; yield* can handle
// strings, Arrays, other generators, etc., all with no special handling
for (var x of chain(a, b, c)) {
// do stuff with current value
}
Sure, the savings in lines of code aren't incredible. It's mostly just reducing boilerplate and unnecessary names, removing the need to deal with closures for simple cases, and with the for...of syntax, providing a common mechanism to iterate arbitrary iterable things, rather than requiring the user to explicitly construct the initial closure and advance it by name. But if the pattern is common enough, that's useful enough.
As noted in comments, a, b, c must be Array-like for the closure based approach (or you'd use a different closure based approach where the writer of chain imposes arbitrary requirements on stuff passed to it, with special cases for Array-like stuff vs. generator-like closures) and processing is destructive (you'd need to add more closure state or make copies to make it non-destructive, making it more complex or slower); for the generator-based approach with yield*, no special cases required. This makes generators composable without complex specs; they can build on one another easily.
async/awaitin the future. I think it's beautiful thatasync/awaitis nothing but generators + promises, but I always saw this as a (maybe well-planned) side effect of generators.