Generator Function: A deep dive

In our previous post on the generator function,

Understanding Generator Functions in JavaScript

let me break down this code step by step, so you can better understand what’s happening when we combine generator functions with Promises. Here’s the code again:

const generator = fetchData();

generator.next().value
    .then(response => generator.next(response).value)
    .then(data => generator.next(data));

Context

This code is part of an example that uses a generator function (fetchData) to handle an asynchronous operation with fetch. The function yields a Promise (the fetch call), and then waits for the Promise to resolve before continuing.

Let’s look at the fetchData function again:

function* fetchData() {
    const response = yield fetch("https://jsonplaceholder.typicode.com/todos/1");
    const data = yield response.json();
    console.log(data);
}

This generator function does two asynchronous things:

  1. fetch(): It fetches data from an API and yields the resulting Promise.
  2. response.json(): After the first yield, it waits for the response object, and then yields a Promise again to parse the JSON.

The tricky part is that the generator pauses at each yield until you manually call .next() to resume its execution, which is why we are chaining .next() calls with the Promise.then() pattern.

Now let’s break down the key lines:

const generator = fetchData(); // Start the generator, but no code inside has run yet

Here, we create an iterator (generator) from the generator function. At this point, fetchData() has not run its code yet; it will start when we first call generator.next().

Step 1: Call .next() and Yield the First Promise

generator.next().value
  • The first generator.next() starts the generator and executes the code up to the first yield.
  • The first yield returns the Promise from the fetch() call.
  • .value extracts the Promise from the object { value: promise, done: false }.

Step 2: Handle the fetch Promise with .then()

.then(response => generator.next(response).value)
  • Once the Promise (returned by fetch()) is resolved, the .then() callback is executed.
  • response is the result of the fetch call, and we pass it back into the generator using generator.next(response).
  • This resumes the generator function, where response is assigned to the const response in the line const response = yield fetch(...).
  • The generator now runs until the next yield, which happens with yield response.json(). This yields another Promise (the response.json() method that parses the response as JSON).
  • We access this second Promise with .value again.

Step 3: Handle the JSON Parsing Promise

.then(data => generator.next(data));
  • Once the second Promise (from response.json()) resolves, the .then() block runs.
  • The parsed JSON data (data) is passed back into the generator with generator.next(data).
  • This completes the generator function, and console.log(data) is executed, printing the final parsed JSON data to the console.
Simplified Breakdown
  1. First yield: fetch() is called, yielding a Promise.
  • Wait for the Promise to resolve.
  1. Second yield: Once the fetch is done, pass the response to generator.next(response).
  • Wait for response.json() to resolve (another Promise).
  1. Complete: Pass the parsed JSON (data) to generator.next(data) and finish execution.
Visual Representation of Execution:
  1. Start generatorfetchData() begins.
  2. First next() → Calls fetch() and yields the resulting Promise.
  3. First .then() → Handles the response and passes it back into the generator.
  4. Second next() → Calls response.json() and yields another Promise.
  5. Second .then() → Handles the parsed JSON and passes it back into the generator.
  6. Complete → Logs the final data.
What’s Really Happening

This pattern is a way of using generators to manage asynchronous code in a sequential way. Without generators, you’d usually see Promises being chained in a .then() fashion. Generators make this process cleaner and easier to read in certain cases, especially when you combine them with async/await (which was designed to replace this manual generator-based flow).

Modern Approach with async/await

You can achieve the same functionality using async/await, which is easier to understand:

async function fetchData() {
    const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
    const data = await response.json();
    console.log(data);
}

fetchData();

This version does the same thing but with cleaner syntax, and it’s easier to manage for most people, as it removes the need for manually calling .next().

Conclusion

The code you saw demonstrates how generator functions work with promises, allowing you to pause and resume execution. However, in modern JavaScript development, async/await has become the preferred way to handle asynchronous operations because it’s much more readable and does not require manual next() calls.

Leave a Reply

Your email address will not be published. Required fields are marked *