---
title: "How JavaScript Generators works with Examples"
date: "2025-11-17"
slug: "why-and-when-use-javascript-generators-with-examples"
author: "Dany Paredes"
canonical: "https://danywalls.com/why-and-when-use-javascript-generators-with-examples"
description: "Today I’ve been reading about Redux-Saga. One of the key concepts there is the use of generator functions (function*). Generators are a JavaScript feature I’d heard of before, but I never really needed them in my day-to-day work—probably because I di..."
---

Today I’ve been reading about [Redux-Saga](https://redux-saga.js.org/). One of [the key c](https://redux-saga.js.org/)oncepts there is the use of generator functions (function\*). Generators are a JavaScript feature I’d heard of before, but I never really needed them in my day-to-day work—probably because I didn’t fully understand **why** or **when** to use them.

Today I finally understood their power, when to use them, and how they can help with common scenarios.

If we read the official documentation about generators on MDN, it says:

> The `Generator` object is returned by a [generator function and it conforms t](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*)o both the [iterable protocol and the iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol) [protocol.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol)

MDN also provides this example:

```typescript
function* infinite() {
  let index = 0;

  while (true) {
    yield index++;
  }
}

const generator = infinite(); // "Generator { }"

console.log(generator.next().value); // 0
console.log(generator.next().value); // 1
console.log(generator.next().value); // 2
// …
```

After reading that definition and example, a common way to describe a generator is:

> A generator is a function whose execution you can **pause and resume**, using the yield keyword.

I shared this idea with my friend @[Bezael Pérez](@bezael) , and he asked me:

> “When do you actually need to pause or resume execution?”

That was a great question, so I decided to read more, investigate, and build a small example to make it clearer for both of us.

## The Function vs Function\*

In JavaScript, normal functions return a single value (or nothing). When you call them, they run from start to finish and then they’re done.

For example, imagine we have a getQuestion function that stores all the questions we want to ask a user. We pass an index as a parameter, with a default value:

```typescript
function getQuestion(question = 0) {
  const questions = ['What is your name', 'What is your email', 'What is your age'];

  return questions[question];
}

console.log(getQuestion());
// Output:
// 'What is your name'
```

This works perfectly. But if I need the next question, I have to call the function again with a different index:

```typescript
console.log(getQuestion(2))
'What is your age'
```

In this example, **the caller (the “client”) is responsible for knowing the state**, and must pass the correct index every time. In a real app, the client might be a UI component.

It would be nice if we could make this easier for the client and encapsulate that “question flow” logic somewhere else.

## The Generator

A generator function uses function\* together with yield to return multiple values over time. This makes it easy to turn data into an iterable sequence or a data stream. Every time the generator hits yield, it **pauses** execution and returns that value.

```typescript
function* register(){
   yield "Hello";
   name = yield "World";
  return `Hello ${name} from Generator` 
}
```

If we call register(), we might expect it to run like a normal function, but instead it returns a **generator object**. That object is responsible for controlling the execution.

```typescript
const registerFlow = register();
```

The generator object provides a `.next()` method, which moves execution to the next yield and returns an object with `{ value, done }`.

Here’s a more complete example:

```typescript
function* gretting() {
    yield "Hi from generator";
  const name = yield "World";
  const lastname = yield  "Hello";
  return `Hello ${name} ${lastname} from generator`
}


const grettingFlow = gretting();
console.log(grettingFlow.next());
//prints Hi from generator

grettingFlow.next("dany");
grettingFlow.next("paredes");
```

Each call to .next() returns an object like this:

```typescript
{ value: 'World', done: false }
{ value: 'Hello', done: false }
{
  value: 'Hello dany paredes from generator',
  done: true
}
```

* value is the current yield value (or the final return value).
    
* done is false while the generator still has steps to execute.
    
* When the generator finishes (return), done becomes true.
    

Now that we have an intuition for generators, let’s play with a real-world use case.

## Build a Form Stepper with Generator

I’m using **TypeScript + React inside a Next.js app**. This example lives in a **Client Component** in the Next.js App Router (for example, app/page.tsx).

```typescript
Generator<Y, R, N>
```

Where:

* Y = the type of values **yielded** by the generator
    
* R = the **return** type when the generator finishes
    
* N = the type of values passed **into** .next(value)
    

Our generator is registerFlow() looks like:

```typescript
export function* registerFlow(): Generator<
  string,
  { name: string; age: string; email: string },
  string
> {
```

this means:

* The generator will yield **strings** (the questions).
    
* The final return value is an object with name, age and email.
    
* Each call to .next(...) will pass a **string** as the answer.
    

Here’s the full generator:

```typescript
export function* registerFlow(): Generator<
  string,
  { name: string; age: string; email: string },
  string
> {
  const name = yield "What is your name?";
  const age = yield "What is your age ?";
  const email = yield "What is your email?";

  return {
    name,
    age,
    email,
  };
}
```

The generator is responsible for:

* Sending questions to the UI.
    
* Receiving answers from the UI.
    
* Returning the final registration data.
    

## Using RegisterFlow with NextJS

In this section, I’ll show how to plug the generator into a **Next.js Client Component** to build a tiny form stepper like this.

![](/images/posts/why-and-when-use-javascript-generators-with-examples/bdf5166a-ee7d-4_58688.gif)

First, we create a ref using `useRef` hook with the generator:

```typescript
 const formStepRef = useRef(registerFlow());
```

Then we set up our component state:

```typescript
  const formStepRef = useRef(registerFlow());
  const [question, setQuestion] = useState<string | undefined>(undefined);
  const [answer, setAnswer] = useState("");
  const [registrationData, setRegistrationData] = useState<
    Registration | undefined
  >(undefined);
```

On mount, we move to the first step using `useEffect` hook:

```typescript
useEffect(() => {
  const initialStep = formStepRef.current.next();
  if (initialStep.done) {
    setRegistrationData(initialStep.value);
    return;
  }
  setQuestion(initialStep.value);
}, []);
```

Then we handle step changes using .next and done:

```typescript
 const changeStep = () => {
    const steps = formStepRef.current.next(answer);

    if (steps.done) {
      setRegistrationData(steps.value);
      return;
    }
    setQuestion(steps.value as string);
    setAnswer("");
  };
```

The final code:

```typescript
"use client";
import { useEffect, useRef, useState } from "react";
import { registerFlow } from "./registerflow";
import { clsx } from "clsx";

export type Registration = {
  name: string;
  age: string;
  email: string;
};

const Page = () => {
  const formStepRef = useRef(registerFlow());
  const [question, setQuestion] = useState<string | undefined>(undefined);
  const [answer, setAnswer] = useState("");
  const [registrationData, setRegistrationData] = useState<
    Registration | undefined
  >(undefined);

  useEffect(() => {
    const initialStep = formStepRef.current.next();
    if (initialStep.done) {
      setRegistrationData(initialStep.value);
      return;
    }
    setQuestion(initialStep.value);
  }, []);

  const changeStep = () => {
    const steps = formStepRef.current.next(answer);

    if (steps.done) {
      setRegistrationData(steps.value);
      return;
    }
    setQuestion(steps.value as string);
    setAnswer("");
  };
 return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gray-100">
      <div className="bg-white p-8 rounded-xl shadow-md flex flex-col gap-6 w-full max-w-md">
        <h1 className="text-3xl font-bold text-gray-800 text-center mb-2">
          Registration Page
        </h1>
        <h3 className="text-lg text-gray-600 text-center min-h-[2em]">
          {registrationData ? `Thanks ${registrationData.name}!` : question}
        </h3>
        {!registrationData && (
          <input
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded focus:outline-none focus:ring-2 focus:ring-blue-400 px-4 py-2 w-full transition"
            value={answer}
            onChange={(e) => setAnswer(e.target.value)}
            placeholder="Enter your answer"
          />
        )}
        <button
          onClick={() => changeStep()}
          className={clsx(
            `mt-4 w-full py-2 px-4 bg-blue-600 text-white text-lg font-semibold rounded hover:bg-blue-700 transition disabled:opacity-50`,
            { invisible: registrationData }
          )}
        >
          {registrationData ? "Finish" : "Next"}
        </button>
      </div>
    </div>
  );
};
```

## Conclusion

I want to confess that Generators can look a bit strange the first time you see function\* and yield, but the mental model is simple:

* A generator lets you **pause and resume** a function.
    
* Each yield is like a “checkpoint” where you can send a value out and later send a value back in.
    
* The .next() calls drive the flow from the outside.
    

I used a generator to model a **multi-step form** with a Next.js app, but the same idea applies to more complex flows: wizards, onboarding, async workflows, and of course, for example, the **Redux-Saga library** was what triggered my interest to learn about them , for example doing a small [experiment](https://github.com/danywalls/wizard-step/blob/main/app/article/articleFlow.ts)

Once you’re comfortable with simple patterns like this form stepper, reading Redux-Saga code becomes much easier

* Source code: [https://github.com/danywalls/wizard-step](https://github.com/danywalls/wizard-step)

_Photo by [Sergei Gussev](https://unsplash.com/@sergei_gussev?utm_source=unsplashx&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=unsplashx&utm_medium=referral)_

