Iterators and generators are important concepts to understand about iteration in core languages such as JavaScript.
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
.
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.
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.
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
methodWe’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();
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.
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();
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 callasync function ApiCallFunction(endpointUrl) {try {// Make API call using Fetch APIconst response = await fetch(endpointUrl);// Extracting JSON data from responseconst content = await response.json();// Returning extracted JSON datareturn content;} catch(error) {// Printing error messageconsole.log(error);// Returning meals empty list// if an error occurs to avoid furster callsreturn { "meals": [] };}}// Generator function that generates response by making multiple API callsasync function* asyncGeneratorForApiCall(mealsIDsList) {// Looping through list of meal IDsfor (const mealId of mealsIDsList) {// Defining MealDB API endpoint to fetch specific meal data according// to the specific meal IDconst endpointUrl = `https://www.themealdb.com/api/json/v1/1/lookup.php?i=${mealId}`;// Making an API call to get meal dataconst mealsDataResponse = await ApiCallFunction(endpointUrl);// Transforming JSON object data into an HTML stringconst 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 stringyield mealsData;}}// Function to make multiple API callsasync function multipleApiCall(mealsIDsList) {// Initializing iterator using the generator functionconst asyncIteratorForApiCall = asyncGeneratorForApiCall(mealsIDsList);// Starting infinite loopwhile(true) {// Initializing an iterableconst iterable = await asyncIteratorForApiCall.next();// Breaking the infinite loop if iterable.done is true as// all iterables have been exhaustedif (iterable.done)break;// Printing value of iterable which is basically the API responseconsole.log(iterable.value);}}// Driver code to test the implementationasync function main() {// Defining MealDB API endpoint to fetch list of Dessert mealsconst endpointUrl = 'https://www.themealdb.com/api/json/v1/1/filter.php?c=Dessert';// Making an API callconst mealsListResponse = await ApiCallFunction(endpointUrl);// Creating a list of IDs for the first three object elementsconst mealsIDsList = mealsListResponse.meals.slice(0, 3).map(object => object.idMeal);// Making multiple API calls using generator and iteratormultipleApiCall(mealsIDsList);}// Testing codemain();
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