First of all: what is Generators in js?🤔
- Generators are functions.
- Generator Function Run Its Code When Required.
- Generators Are iterables.
- It doesn't run unless you need it.
- Generator Function Return Special Object [Generator Object].
- That Object can loop on generator function data.
❗NOTE: The data is present but when the function run, it generates the data.
Briefly:
Generators functions are different from normal function it doesn't run until you need it, they are iterable & return special object.
Example from real live:
like tickets generator device, when you pay it generates new serial number and print on new ticket then you get it, Here serial number already exists but it didn't print until get order to create a new ticket.
Generators Methods:
next()
to get next yield.
return()
to return a value & stop generating.
throw()
to throw an error & stop generating.
Generators properties:
done
if iteration done or not, return boolean value.
value
current yield value.
Syntax:
function* generatorName(){
}
Now you can put in it any JS code and also can add the data that want to generate and we'll see how in next paragraph👇.
Yield Data | Data Production Process
Yield
keyword used to data production process, and then you can loop on it using next()
with Generator Object.
so you use yield
keyword and put it's value.
function* generatorName(){
yield "hello";
yield "bye";
yield 1;
yield 2;
}
you can put any type of data (boolean, object...)
lets try Looping on Generator function Yields🔃
for (let i of generateNums()){
console.log(i);
}
the output will be:
"hello"
"bye"
1
2
this also what next()
method does!
Create Generator Object🏭
let generator = generatorName();
lets see something will make this clear to you💯
lets check typeof
console.log(typeof generator); // object
console.log(typeof generatorName); // function
console.log(generator); // Object: Generator {...}
next method | Accessing Yields
It returns value & done state
console.log(generator.next()); // Object { value: "hello", done: false }
console.log(generator.next()); // Object { value: "bye", done: false }
console.log(generator.next()); // Object { value: 1, done: false }
console.log(generator.next()); // Object { value: 2, done: false }
As you note every time you use next()
method, it returns the next yield and can't undo this action.⏩
Now we looped on all yields, if we use next()
again guess what we'll happen!🤔
console.log(generator.next()); // Object { value: undefined, done: true }
yes, it returned value: undefined
that's because there is no more yield
to access, also you'll find done: true
because loop is over.
value property:
Use it with next()
method to get the current yield value.
console.log(generator.next().value); // undefined
I want you to think why it returned undefined.🤔
I'll be 😊 if you answered it true in comments.
done property:
console.log(generator.next().done); // true
return & throw Methods | Controlling yield process
1- return
Use return
method to stop yielding process, making done:true
& value: undefined
also you can return a custom value.
Let's see an example😬
these are our generator function & generator object:
function* letter(){
yield "A";
yield "B";
yield "C";
}
let gen2 = letter();
also we can access normally:
console.log(gen2.next()); // Object { value: "A, done: false }
console.log(gen2.next()); // Object { value: "B", done: false }
Now if we used return
method this is what will happen
console.log(gen2.return()); // Object { value: undefined, done: true }
As you noticed we still have one yield
we didn't access it before, plus if you used next()
method again you'll get { value: undefined, done: true }
.
that is what return
is here to do!
also you can return any value, by putting it between ()
console.log(gen2.return("visit youtube.com/devcoder"));
now output will change to { value: "visit youtube.com/devcoder", done: true }
.
2- throw
It's like return()
method, but it throws custom errors like throw
keyword.
let gen3 = letter();
console.log(gen3.next()); // Object { value: "A, done: false }
console.log(gen3.throw("Hi coder!")); // Error: Uncaught Hi coder!
console.log(gen3.next()); // not working
❗NOTE: throw()
method doesn't stop your code, just stop the generator object.
Use Case: for example we have a generator function to generate random numbers, if that number is duplicated, it throws error.
Loop on generator yields
if we tried to loop through gen2
no output will appear
for (let i of gen2){
console.log(i);
}
that's because we already accessed gen2
in past examples, so will create new generator object and loop though
let gen4 = letter()
for (let i of gen4 ){
console.log(i);
}
try that, will work.✅
Generate Infinite Numbers
function* infinity(){
let index = 0
while(true){
yield index++
}
}
let inf = infinity();
Now as you see we're able to add normal JS code inside generator function,
1- when inf
visit infinity()
for first time index = 0
and will enter the infinite loop and will not exit
2- in loop, every time we use next()
, index will increment and yield will equal the new index value.
NOW, let's use next()
on it
console.log(inf.next()); // Object { value: 0, done: false }
console.log(inf.next()); // Object { value: 1, done: false }
console.log(inf.next()); // Object { value: 2, done: false }
console.log(inf.next()); // Object { value: 3, done: false }
...
🔴IMPORTANT
yield*
What is deference between yield
& yield*
, the answer will be clear if we try it with an array.👇
function* GeneAll(){
yield* [4,5];
yield [6,7];
}
let gen754 = GeneAll()
Now let's try to access for one time
console.log(gen754.next()); // Object { value: 4, done: false }
as you noticed it returned only the first element from our array.
let's try again!
console.log(gen754.next()); // Object { value: 5, done: false }
Now it returned the second element.
let's try again!
console.log(gen754.next()); // Object { value: [6,7], done: false }
But now it returned the full array, why???🤯
the yield*
treat value as individual elements, so it converts the array value into more small values, but yield
doesn't do this.
The same thing applies to string
!
Delegate Generator Function | (Generator Function Chaining)
- it's a function that delegate another function yield
take an example🎴
function* nums(){
yield 1;
yield 2;
yield 3;
}
function* letter(){
yield "A";
yield "B";
yield "C";
}
function* AllYields(){
yield nums(); // without starter"*" will return the name of function
yield* nums(); // this will include an array contains all nums() yields
yield* letter()
yield [6,7]; // without starter"*" it'll return an array
}
let gen55 = AllYields()
Let's see what will happen in first next()
console.log(gen55 .next()); // Object { value: Generator, done: false }
as you saw it returned the function name
console.log(gen55.next()); // Object { value: 1, done: false }
console.log(gen55.next()); // Object { value: 2, done: false }
console.log(gen55.next()); // Object { value: 3, done: false }
What happened behind the since?🤯
1- gen55
will go to every yield
and enter it to access all data in it.
2- it will find that first yield
without * and contains a generator function nums()
.
3- will return it's name.
4- after that gen55
will go to next yield
"yield* nums()" with * so, will go through it.
5- it will find that it contains other yields so, will go through every yield
and return it on every next()
method call until it end accessing all nums()
yields.
6- and now it went through out our second yield
"yield* nums()" .
7- Then go to next yield
in AllYields()
and repeat the same process etc...
Conclusion
This was all what I know about generators in javascript, I hope you enjoyed with this article and learned a new thing,
I'll be happy for that.
If you have any notes about this article or new information tell me about in comments.
Follow me on YouTube , GitHub .
For Coding T-shirts visit: CodingShirts .
And don't forget to read these references👇.
See you again, Bye👋
References
MDN: Generator
MDN: function*
MDN: Iterators and generators
digitalocean: understanding generators in JS
JavaPoint: ES6 Generators
Top comments (0)