How to asynchronously call APIs using generators in JavaScript

What are iterators and generators?

Iterators and generators are important concepts to understand about iteration in core languages such as JavaScript.

Iterators

We can define an iterator as a sequential object that returns a value upon termination. In Javascript, it’s an object that’s iteratively called using the next() method on it, which returns an object with the done and value attributes. 

The value attribute is the next value in the interaction sequence, while the done attribute is used to signify if the last value in the iteration sequence has been consumed. If we explicitly return a value from the iterator, then that value is present alongside the done attribute with the true value; otherwise, the object with the done attribute as true will have the value attribute as null.

Generators

In order to avoid making our own custom iterators which is a complex process, we can use the JavaScript-provided generator functions, which we define using the function* syntax. Generator functions allow us to define the logic of an iteration sequence by defining a single non-continuous function. 

Generator functions don’t directly execute their code like normal functions, and instead, they return a special iterator type called a generator, which has the same iterator properties we previously went over. The generator function will keep executing until the yield keyword is encountered.

Why use them for asynchronous API calls?

JavaScript generator/iterator has several benefits when it comes to making asynchronous API calls:

  • We can stream bulk data returned by an API.

  • It simplifies and automates an API’s pagination response without manually managing it.

  • It allows us to lazy load API data, especially when fetching images.

  • It allows us to asynchronously make HTTP requests to an API.

  • We can set up WebSockets with generators and iterators to fetch and handle real-time data.

Implementation

Now that we’ve gone over what generators and iterators are, let’s go over how we can implement JavaScript generators to make asynchronous API calls.

Fetch API’s fetch method

We’ll use the Fetch API’s fetch method to make asynchronous API calls. Here’s a sample implementation of the fetch method to make an API call to an API endpoint URL:

const response = await fetch("http://example-api.com/");
const content = await response.json();

Generator functions

The following is the implementation of a generator function using the function* syntax:

async function* asyncGenerator() {
const response = await fetch("http://example-api.com/");
yield await response.json();
}

Notice that we add the yield keyword on the second await statement so that the entire generator function has executed before the next iterable in the iteration sequence.

Iterators

The following is the implementation of the iterator of the generator function, also called the generator, which we can iterate using the next() method:

const asyncIterator = asyncGenerator();
const iterable = await asyncIteratorForApiCall.next();

Code example

Here’s an executable code where we make API calls to the MealDB service to fetch recipes for multiple meals. We first make an API call to get a list of IDs for different meals, after which we use the generator to make multiple API calls to fetch bulk meal data for each ID.

Note: The code executable below is running on NodeJS v18.16.0.

Simply click the “Run” button to execute the code.

// Function to make API call
async function ApiCallFunction(endpointUrl) {
try {
// Make API call using Fetch API
const response = await fetch(endpointUrl);
// Extracting JSON data from response
const content = await response.json();
// Returning extracted JSON data
return content;
} catch(error) {
// Printing error message
console.log(error);
// Returning meals empty list
// if an error occurs to avoid furster calls
return { "meals": [] };
}
}
// Generator function that generates response by making multiple API calls
async function* asyncGeneratorForApiCall(mealsIDsList) {
// Looping through list of meal IDs
for (const mealId of mealsIDsList) {
// Defining MealDB API endpoint to fetch specific meal data according
// to the specific meal ID
const endpointUrl = `https://www.themealdb.com/api/json/v1/1/lookup.php?i=${mealId}`;
// Making an API call to get meal data
const mealsDataResponse = await ApiCallFunction(endpointUrl);
// Transforming JSON object data into an HTML string
const mealsData = `<div><h1>${mealsDataResponse.meals[0].strMeal}</h1><img src="${mealsDataResponse.meals[0].strMealThumb}/preview"><p>${mealsDataResponse.meals[0].strInstructions}</p><hr></div>`;
// Yielding meal data that can be displayed as an HTML string
yield mealsData;
}
}
// Function to make multiple API calls
async function multipleApiCall(mealsIDsList) {
// Initializing iterator using the generator function
const asyncIteratorForApiCall = asyncGeneratorForApiCall(mealsIDsList);
// Starting infinite loop
while(true) {
// Initializing an iterable
const iterable = await asyncIteratorForApiCall.next();
// Breaking the infinite loop if iterable.done is true as
// all iterables have been exhausted
if (iterable.done)
break;
// Printing value of iterable which is basically the API response
console.log(iterable.value);
}
}
// Driver code to test the implementation
async function main() {
// Defining MealDB API endpoint to fetch list of Dessert meals
const endpointUrl = 'https://www.themealdb.com/api/json/v1/1/filter.php?c=Dessert';
// Making an API call
const mealsListResponse = await ApiCallFunction(endpointUrl);
// Creating a list of IDs for the first three object elements
const mealsIDsList = mealsListResponse.meals.slice(0, 3).map(object => object.idMeal);
// Making multiple API calls using generator and iterator
multipleApiCall(mealsIDsList);
}
// Testing code
main();

Here’s a brief explanation of the code above:

  • Line 2: We define the general function of making API calls using the Fetch API and returning the JSON data from the response.

  • Line 5: We make an API call using the fetch method and save its response in the response variable.

  • Line 8: We extract the JSON data from the response variable using the json() method.

  • Lines 23–39: We define the asyncGeneratorForApiCall async generator function to make multiple API calls for each of the IDs from mealsIDsList in the function argument.

    • Line 28: We define the API endpoint URL under the endpointUrl variable to fetch detailed meal data for a specific meal ID.

    • Line 31: We make an API call using the ApiCallFunction function and the JSON data object.

    • Line 34: We transform the response JSON object into an HTML string.

    • Line 37: We use the yield keyword to yield the mealsData object to the iterator.

  • Line 42: We define the multipleApiCall async function to make multiple API calls to fetch data for each meal ID from the MealDB API.

  • Line 44: We initialize the asyncIteratorForApiCall generator/iterator.

  • Lines 47–58: We initialize an infinite loop to iterate through the iterator until we break the loop when the last iterator value is consumed.

    • Line 49: We use the next() method to initialize an iterable object.

    • Lines 53–54: We check if the iterable object’s done attribute is true and break the infinite loop if it is.

    • Line 57: We print the HTML string yielded by the generator function.

  • Line 62: We define the driver code under the main() function to test the implementation of our generator and API call functions.

  • Line 77: We start the driver code execution by calling the main() function.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved