ECMAScript 6 - Generators
Generators are one of the most interesting, intriguing, and innovative features of ECMAScript 6, and they get even more interesting the more we use the language for time consuming and heavy tasks. But the generators feature can be confusing, so for those of you who always scroll down straight to the code examples, today try reading the explanations as well. This whole generators thing can be a little tough to grasp at the beginning.
Let’s have a look at this code example, that was written by Kyle Simpson and taken from the website of David Walsh. It’s an excellent explanation of how generators work:
setTimeout(function(){
console.log("Hello World");
},1);
function foo() {
// NOTE: don't ever do crazy long-running loops like this
for (var i=0; i< =1E10; i++) {
console.log(i);
}
}
foo();
// 0..1E10
// "Hello World"
So what’s going on here? We have a function call, that waits a full millisecond and then prints a string. But in actuality, the string won’t be printed until long after that whole long loop, which will be way more than a millisecond. Why is this? Because when the printing is about to happen, there is a loop that is still running. We can never interrupt a loop while it’s still running—it needs to run till till its finished before we can do anything with it. This is one of the most basic conventions in programming—at least until now.
Generator in ECMAScript 6 allow us to create functions, loops, or other blocks of code that we can stop while they’re running. The function stops wherever we tell it to, and then returns a result. The code outside of the function can receive the result and then restart the function.
Sound complicated? It’s really not. Functions like this, that can stop and then restart themselves again are called generators. They look just like regular functions, just that have an asterisk before their name. The keyword used to stop them is yield. As soon as the function calls yield, two things happen: the yield returns a value and the function stops until something from the outside starts it again.
This means the function stops only when it supposed to; nothing can stop the generator from the outside. But, the generator can only be started again when something from outside the function starts it. Generators cannot restart themselves. The initial start of the function, and every subsequent restart after a yield, is done by next().
So now you think that this sounds confusing? Let’s skip the headache and check out some code:
function *myGenerator() {
var x = 'start';
console.log(x);
yield 'yieldResult';
x = 'end';
console.log(x);
}
Here we have the function, that looks like any other normal function. But notice something interesting, there is an asterisk at the start of the name. This is what lets us know that this is not just a simple function, but rather a generator. Note that one of the properties is yield. As started before, we call generators with the next() function:
var it = myGenerator();
it.next();
What do you think is going to be printed out here? You’re welcome to paste the code into the console and see, or you can just believe me when I tell you that what appears in the console is start. So why doesn’t the function continue as it normally would? Because we have a yield that stops it! Wanna make it start up again? No problem! We just use next():
function *myGenerator() {
var x = 'start';
console.log(x);
yield 'yieldResult';
x = 'end';
console.log(x);
}
var it = myGenerator();
it.next(); //start
it.next(); //end
Hold on though—notice that the yield also returns something. How are we receiving that piece of data? Quite easily actually. If we return the it.next to a variable, we see that it’s made up of an object that has a result, which is what yield passes to us. Last there’s status which tells us if the generator finished its operation or not.
var resultA = it.next(); //start
console.log(resultA); //Object { done: false, value: "yieldResult"}
var resultB = it.next(); //end
console.log(resultB); //Object { done: true, value: undefined}
We can also allow for two way communication, meaning that we can pass a value to yield at the start of the generator which it will use. Check out this example:
function *myGenerator() {
var a = yield 'yieldResult';
console.log(a);
}
var it = myGenerator();
it.next(); //Initial start
it.next(6); //end, the console will be 6
Here, the yield that’s stopping the generator is also returning a value to a. Which value, you ask? Any value that the second start of the generator returns to it (the first start launches the generator, while the second starts it after the first yield). Since we pass 6 at the first start, we also receive it inside the generator. In other words, the yield can return and also receive a value, and is the sole mechanism available to us in all that happens regarding information coming from or going into the generator.
The generator can return a value via return, but it’s best not to use this since in for of loops the last value in the iteration will not be returned. Come again? What’s this for of loop thing you speak of? And how is it connected to generators? Oh and how is any of this connected to the quest for true love?
All this and more (except for the true love thing) in the next article.
About the author: Ran Bar-Zik is an experienced web developer whose personal blog, Internet Israel, features articles and guides on Node.js, MongoDB, Git, SASS, jQuery, HTML 5, MySQL, and more. Translation of the original article by Aaron Raizen.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment