Understanding Generators in JavaScript with examples
Tags: javascript, webdev, generators
Published: 2022-10-09 18:21:36

# First of all: what is Generators in js?🤔

1. Generators are functions.
2. Generator Function Run Its Code When Required.
3. Generators Are iterables.
4. It doesn't run unless you need it.
5. Generator Function Return Special Object [Generator Object].
6. 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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dyhld3zux9kl39p2d3mc.png)

### 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:

```javascript
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.

```javascript
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🔃

```javascript
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🏭
```javascript
let generator = generatorName();
```
lets see something will make this clear to you💯
lets check `typeof` 

```javascript
console.log(typeof generator); // object
console.log(typeof generatorName); // function
console.log(generator); // Object: Generator {...}
```

# next method | Accessing Yields
It returns value & done state
```javascript
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!🤔

```javascript
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.

```javascript
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:
```javascript
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:
```javascript
function* letter(){
  yield "A";
  yield "B";
  yield "C";
}
let gen2 = letter();
```

also we can access normally:
```javascript
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
```javascript
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 ()
```javascript
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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw) keyword.

```javascript
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
```javascript
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
```javascript
let gen4 = letter()
for (let i of gen4 ){
  console.log(i);
}
```
try that, will work.✅


# Generate Infinite Numbers
```javascript
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
```javascript
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.👇

```javascript
function* GeneAll(){
  yield* [4,5];
  yield [6,7];
}

let gen754 = GeneAll()
```
Now let's try to access for one time
```javascript
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!
```javascript
console.log(gen754.next()); // Object { value: 5, done: false }
```
Now it returned the second element.
let's try again!
```javascript
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🎴
```javascript
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()`
```javascript
console.log(gen55 .next()); // Object { value: Generator, done: false }
```
as you saw it returned the function name

```javascript
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](https://www.youtube.com/Devcoder) , [GitHub](https://github.com/HamzaElkotp) .
**For Coding T-shirts visit: [CodingShirts](my-store-c54a10.preview.teespring.com/) .**
And don't forget to read these references👇.
See you again, Bye👋

#References
[MDN: Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator)
[MDN: function*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*)
[MDN: Iterators and generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)
[digitalocean: understanding generators in JS](https://www.digitalocean.com/community/tutorials/understanding-generators-in-javascript)
[JavaPoint: ES6 Generators](https://www.javatpoint.com/es6-generators)