JavaScript Generators: Unleashing the Power of Iterables
Tags: tricks, javascript, webdev
Published: 2023-07-25 11:25:59

---
title: JavaScript Generators: Unleashing the Power of Iterables
published: true
date: 2023-07-18 09:54:48 UTC
tags: Tricks, JavaScript, webdev
canonical_url: https://yon.fun/js-generators/
---
![JavaScript Generators: Unleashing the Power of Iterables](https://yon.fun/content/images/2023/06/JavaScript-Generators-.png)

JavaScript Generators might not be the most popular feature of JavaScript, but they sure pack a punch!

These special functions, which can pause and resume their execution, are like hidden gems in the JavaScript landscape.

They might not be in every developer's toolbox, but once you understand their power, you'll wonder how you ever coded without them.

Generators can make your code cleaner and easier to understand, especially when you're dealing with complex tasks or asynchronous operations. But despite their benefits, they're often overlooked and underused in the JavaScript community.

**So, why not give generators a chance?**

[Anjana Vakil](https://twitter.com/AnjanaVakil) talked about the awesome power of JS Generators in her [JSConf presentation](https://www.youtube.com/watch?v=gu3FfmgkwUc). This article is all about the examples she shared.

If you don't feel like reading, _I highly recommend watching her presentation instead_. But if you do want to read, _don't forget to check out her presentation too_.

## Understanding JavaScript Generators

[JavaScript Generators](https://yon.fun/js-generators/) are special functions that can pause execution and resume at any time, creating a powerful way to manage synchronous and asynchronous tasks in a more readable and maintainable way.

They were introduced in ECMAScript 2015 and have since become a fundamental part of the language.

**A generator function is defined with a function keyword followed by an asterisk.**

When called, it does not execute its code immediately. Instead, it returns a special type of iterator, called a generator.

Here's a simple example of a generator function:

```js
function* simpleGenerator() {
    yield 'first yield';
    yield 'second yield';
    yield 'third yield';
}
```

In this example, the `simpleGenerator` function is a generator function, as indicated by the asterisk (\*) after the function keyword.

The `yield` keyword is used to pause and resume the generator function. Each `yield` keyword represents a stage of the generator's execution.

## Using Generators

To use a generator, you first have to initialize it. This is done by calling the generator function, which returns a generator object:

```js
let gen = simpleGenerator();
```

The returned generator object has a `next()` method, which resumes the generator function until the next `yield` statement is reached.

The `next()` method returns an object with two properties: `value` and `done`.

```js
console.log(gen.next()); // { value: 'first yield', done: false }
console.log(gen.next()); // { value: 'second yield', done: false }
console.log(gen.next()); // { value: 'third yield', done: false }
console.log(gen.next()); // { value: undefined, done: true }
```

The `value` property contains the value yielded by the `yield` statement, and `done` is a boolean indicating whether the generator function has been fully completed.

## Leveraging Generators as Iterables

Generators can be used to create complex iterable objects.

For instance, consider a scenario where you need an object representing a deck of cards.

**Instead of manually enumerating all 52 card values, you can create a generator function that computes all permutations of the different suits and numbers, yielding each one by one.**

```js
function* deckGenerator() {
  const suits = ['♠', '♣', '♥', '♦'];
  const ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'];

  for (let s = 0; s < suits.length; s++) {
    for (let r = 0; r < ranks.length; r++) {
      yield ranks[r] + suits[s];
    }
  }
}

const deck = deckGenerator();
console.log(deck.next().value); // "2♠"
console.log(deck.next().value); // "3♠"
```

In this example, we're creating a generator function `deckGenerator` that represents a deck of cards.

The generator function uses two `yield` statements to produce all possible combinations of suits and ranks in a deck of cards.

When we call `deck.next().value`, the generator function resumes, returning the next card in the deck.

This makes it easy to iterate over the deck of cards without having to generate all possible combinations upfront.

What about if we want to show all 52 cards? ♠️

```js
console.log([...deckGenerator()]) // Generate all possible variants

// Result: (52) ["2♠", "3♠", "4♠", "5♠", "6♠", "7♠",...]
```

This is one of the best examples of how using js generators simplifies devs' life!

## Generators and Asynchronous Operations

With ES7, we got async versions of generators and iterators.

This allows us to handle asynchronous operations with generators. For instance, you can create an async generator function to load paginated data from an API.

```js
async function* asyncGenerator() {
  let i = 0;

  while (i < 3) {
    await new Promise(resolve => setTimeout(resolve, 1000));
    yield i++;
  }
}

const asyncGen = asyncGenerator();
console.log(await asyncGen.next()); // { value: 0, done: false }
console.log(await asyncGen.next()); // { value: 1, done: false }
```

Here, we're using an async generator function `asyncGenerator` to simulate loading paginated data from an API.

The `yield` statement is used in conjunction with `await` to pause the generator function until the Promise resolves.

This allows us to handle asynchronous operations in a synchronous manner, making the code easier to read and understand.

When we call `asyncGen.next()`, it returns a Promise that resolves to the next piece of data.

## Generators as State Machines

Generators can also function as state machines due to their ability to remember their state.

For example, you can create a generator function that represents a bank account.

```js
function* bankAccount() {
  let balance = 0;

  while (true) {
    const deposit = yield;
    balance += deposit;
  }
}

const account = bankAccount();
account.next(); // initialize the generator
account.next(100); // deposit 100
account.next(50); // deposit 50
```

In this example, we're using a generator function `bankAccount` to represent a bank account.

The generator function uses a `yield` statement inside a `while (true)` loop, allowing it to run indefinitely.

Each time we call `account.next(deposit)`, we're depositing money into the account.

The generator function remembers its state (the account balance), making it an effective way to represent a state machine.

## Advanced Generator Usage

Generators can also be used to handle asynchronous tasks in a synchronous manner.

This is done by **combining generators with Promises.**

This combination allows us to write asynchronous code as if it were synchronous, making it much easier to understand and maintain.

```js
function* asyncGenerator() {
    let data = yield new Promise(resolve => setTimeout(() => resolve('async data'), 1000));
    console.log(data);
}

let gen = asyncGenerator();
let promise = gen.next().value;
promise.then(data => gen.next(data));
```

In this example, the `asyncGenerator` function yields a Promise that resolves after 1 second.

The `next()` method is then called on the generator, which returns the Promise.

Once the Promise resolves, the `next()` method is called again with the resolved value, which is then logged to the console.

## Conclusion

Generators are a powerful feature of JavaScript that can help us manage asynchronous operations, implement custom iterable objects, and handle complex control flows.

They can yield control and regain control, functioning as co-routines, passing information back and forth, and doing co-op multitasking.

Watch [Anjana Vakil's JSConf presentation](https://www.youtube.com/watch?v=gu3FfmgkwUc) for an in-depth understanding of the Power of JS Generators. Her presentation provides comprehensive insights and explanations that will greatly enhance your knowledge in this area.

Remember, practice is the key to mastering generators, like any programming concept. 

**So, don't hesitate to experiment with generators in your own code and explore the many possibilities they offer.**