What are the Generators?
Generators are introduced in ES6(ECMAScript 2015). It is the new way to work with the functions. With the help of generator function we can pause and resume the execution of code inside the function from anywhere.
If we will talk about the regular functions, you will see whenever we will call regular function it will execute from top to bottom. Execution can not stop anywhere and can not start from anywhere. If we want to stop the execution we need to return the function.
But generator functions give the provision to pause and resume the execution of code in midway. Thats why we call regular functions as “run until return/end” and generator functions as “run until yield/return/end“.
Generator functions are denoted by * as you can see in below syntax.
function* generator() { } or function *generator() { }
Example of Regular function
Let’s first see the case of regular function which we have discuss above.
function regularFunction() { console.log(1); console.log(2); console.log(3); } regularFunction();
Here you can see this is a regular function. Where we have just called the regularFunction(). And it will execute the statements in sequence and output is nothing unexpected , i.e 1,2,3. There is no midway pause and resume. Either this function will fully execute or we can return it from anywhere.
How generator function works?
function* generatorFunction() {
yield 1;
yield 2;
}
const generatorFun = generatorFunction();
console.log(generatorFun.next());
So in the above code we have created one generatorFunction(). And we have used * with the function which denotes that its a generator function not a normal function. Inside this function we have used yield keyword. which is used to pause the execution and return the value.
Then we have invoked the generatorFunction(). If we will console log the generateFun it will return the generator Object like below.
The Generator object returns another object. For that we used the next() function. So output for generatorFun.next() will be –
So Generator object return two properties i.e done and value. Done indicate the status of generator whether it can be executed further or not. And value return undefined for the empty yield else return the value whatever is besides the yield. So the 1st time yield 1 will execute and it will stop the execution.
Now let see if we will invoke the generator function for the 2nd time. Let see what will be the output-
function* generatorFunction() { yield 1; yield 2; } const generatorFun = generatorFunction(); console.log(generatorFun.next()); console.log(generatorFun.next());
Since last time execution stops after the 1st yield statement. Now this execution will start from the 2nd yield statement.
Now if will try to invoke generatorFunction one more time –
console.log(generatorFun.next());
let see below output
Here you can see the last object is done=true and value=undefined. Because there is no any more yield statements and execution is completed now. There are no any values to be invoked basically there is an empty yield hence its returning value as undefined.
Is Generator is Iterable?
yes generator is iterable. Let see the below code-
const generatorFun = generatorFunction();
Here we can iterate the generateFun as its an object.
for (let gen of generatorFun) { console.log((gen)); }
So it will return the values i.e 1,2 .
How to terminate the generator?
function* generatorFunction() { yield 1; yield 2; } const generatorFun = generatorFunction(); console.log(generatorFun.next()); console.log(generatorFun.throw(new Error("Something went wrong: generator terminated"))); console.log(generatorFun.next());
Here you can see the first console log will print 1st object. Now in second console log we have thrown the error. After that no any generatorFun will get execute because here the generator function is terminated as it thrown an error.
Difference between Yield and Yield*
We have seen the working of Yield in above examples. Now here is the another term i.e Yield*. It is used when we want to delegate the another generator function inside the first generator function. Let see the below example.
function* generatorFunction1() { yield 1; yield* generatorFunction2(); } function* generatorFunction2() { yield 2; yield 3; } const gen = generatorFunction1(); console.log(gen.next()); console.log(gen.next()); console.log(gen.next());
Here inside the generatorFunction1 we are calling the another generator function i.e generatorFunction2. It will run in the sequential manner. And the output will be below.
Summery
- Generators are memory efficient.
- Its easy to pause and resume the function whenever required.
- Generators do not execute its body immediately when they are invoked.
- Generators are iterables hence we can use the for…of to iterate it.
- Yield is used to pause the execution and return the value
- Yield* is used when one generator function is used inside the another generator function.