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 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
fetchmethod and save its response in theresponsevariable.Line 8: We extract the JSON data from the
responsevariable using thejson()method.Lines 23–39: We define the
asyncGeneratorForApiCallasync generator function to make multiple API calls for each of the IDs frommealsIDsListin the function argument.Line 28: We define the API endpoint URL under the
endpointUrlvariable to fetch detailed meal data for a specific meal ID.Line 31: We make an API call using the
ApiCallFunctionfunction and the JSON data object.Line 34: We transform the response JSON object into an HTML string.
Line 37: We use the
yieldkeyword to yield themealsDataobject to the iterator.
Line 42: We define the
multipleApiCallasync function to make multiple API calls to fetch data for each meal ID from the MealDB API.Line 44: We initialize the
asyncIteratorForApiCallgenerator/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 aniterableobject.Lines 53–54: We check if the
iterableobject’sdoneattribute istrueand 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