DEV Community

Cover image for JavaScript Generators: The "Pause" Button for Your Code
Samuel Ochaba
Samuel Ochaba

Posted on

JavaScript Generators: The "Pause" Button for Your Code

If you understand standard JavaScript functions, you know the "Run-to-Completion" rule. Once a normal function starts running, nothing can stop it until it hits a return statement or finishes the code block. It’s like a rollercoaster: once the harness locks, you are on the ride until the end.

Generators break this rule.

Generators are functions that can pause in the middle of execution, let other code run, and then resume exactly where they left off, remembering all their variables.

They turn your function from a Rollercoaster into a Netflix series: you can watch 10 minutes, pause it, go make a sandwich, and come back to the exact same second you left.

1. The Syntax: Asterisks and Yields

To create a generator, you use two special pieces of syntax:

  1. function*: The asterisk tells JS "This is a generator."
  2. yield: This is the "Pause" button.
function* myGenerator() {
  console.log("1. Starting...");
  yield "First Pause"; // <--- Pauses here and spits out "First Pause"

  console.log("2. Resuming...");
  yield "Second Pause"; // <--- Pauses here and spits out "Second Pause"

  console.log("3. Finishing!");
  return "Done";
}
Enter fullscreen mode Exit fullscreen mode

2. The Mechanics: It Doesn't Run Immediately!

This is the part that confuses everyone. When you call a generator function, it does not run the code.

Instead, it returns a Generator Object (an iterator). This object is like a remote control for the function.

const gen = myGenerator(); // No console logs happen here!
Enter fullscreen mode Exit fullscreen mode

To run the code, you have to press "Play" on the remote control. In JavaScript, the "Play" button is .next().

console.log(gen.next()); 
// Output: 
// "1. Starting..."
// { value: "First Pause", done: false }
Enter fullscreen mode Exit fullscreen mode

Understanding the Result

When you call .next(), you get an object back:

  • value: Whatever was to the right of the yield keyword.
  • done: false means "Paused, more code to come." true means "Finished."

Let's finish the function:

console.log(gen.next());
// Output:
// "2. Resuming..."
// { value: "Second Pause", done: false }

console.log(gen.next());
// Output:
// "3. Finishing!"
// { value: "Done", done: true } <--- We hit the 'return', so done is true.
Enter fullscreen mode Exit fullscreen mode

3. Two-Way Communication: Passing Data IN

Most people know yield sends data out. But did you know you can send data in when you restart the function?

Think of yield as a portal.

  1. The generator throws a value out.
  2. The generator pauses and waits.
  3. When you call .next(data), that data travels back through the portal and becomes the result of the yield expression.
function* chattyGenerator() {
  const name = yield "What is your name?"; // Pauses here, sends question out
  const age = yield "How old are you?";    // Pauses here, waits for input
  return `Hello ${name}, you are ${age}!`;
}

const gen = chattyGenerator();

// 1. Start it. It runs until the first yield.
console.log(gen.next().value); 
// Output: "What is your name?"

// 2. Resume it, passing data IN.
// This string "John" replaces the 'yield' expression above.
console.log(gen.next("John").value); 
// Output: "How old are you?"

// 3. Resume again.
console.log(gen.next(30).value);
// Output: "Hello John, you are 30!"
Enter fullscreen mode Exit fullscreen mode

4. The Infinite Stream (Memory Efficiency)

This is a real-world use case. If you wanted a list of unique IDs, a standard function would need to generate them all and store them in an array. That takes memory.

A Generator creates them Lazy (on demand). It only calculates the next one when you ask for it. You can write an infinite loop that doesn't crash the browser!

function* idGenerator() {
  let id = 1;
  while (true) { // Infinite loop!
    yield id;
    id++;
  }
}

const ids = idGenerator();

console.log(ids.next().value); // 1
console.log(ids.next().value); // 2
console.log(ids.next().value); // 3
// It pauses at the 'yield' every time.
Enter fullscreen mode Exit fullscreen mode

5. Generator Delegation (yield*)

Sometimes you want one generator to call another generator. You use yield* (yield star). Think of this as the main DJ handing the aux cord to a guest DJ.

function* guestDJ() {
  yield "Song A";
  yield "Song B";
}

function* mainDJ() {
  yield "Intro";
  yield* guestDJ(); // Hand over control to guestDJ until it finishes
  yield "Outro";
}

const playlist = mainDJ();
// Loop over the generator (yes, you can do this!)
for (const song of playlist) {
  console.log(song);
}

// Output:
// Intro
// Song A
// Song B
// Outro
Enter fullscreen mode Exit fullscreen mode

6. Generators & Async (The Precursor to Async/Await)

Before async/await existed, libraries like Redux Saga or co used generators to handle async code. It helps to understand this to see how JS evolved.

If you yield a Promise, you can pause the function until the data arrives.

Note: You need a helper function to run this automatically, but here is the concept:

function* fetchUser() {
  // Yield the promise. The 'runner' waits for it to resolve.
  const user = yield fetch('/api/user'); 

  // The 'runner' passes the resolved JSON back into the function
  console.log(user.name); 
}
Enter fullscreen mode Exit fullscreen mode

This looks almost exactly like async/await, doesn't it?

  • function* is roughly async function
  • yield is roughly await

Summary: The Generator Cheatsheet

  1. function*: Creates the generator.
  2. yield value: Pauses execution and outputs value.
  3. const x = yield: Pauses and waits for data to be passed in via .next().
  4. generator.next(): Runs code until the next yield.
  5. yield*: Delegates to another generator.

Generators give you manual control over the flow of time in your code. While async/await has replaced them for simple data fetching, Generators are still the kings of complex flows, infinite data streams, and state machines.

Top comments (0)