Asynchronous APIs Using the Fetch API and ES6 Generators

Share this article

ECMAScript 6 (a.k.a. ECMAScript 2015 or ES6) brings a number of new features to JavaScript which will make the language a good fit for large applications. One of these features is better support for asynchronous programming using promises and generators. Another is the addition of the Fetch API which aims to replace XMLHttpRequest as the foundation of communication with remote resources.

The Fetch API’s methods return ES6 Promise objects, which can be used in conjunction with generators to form the basis of complex asynchronous operations. This could be anything from a chain of asynchronous operations, where each operation depends on the value returned by the previous one, to an asynchronous call that has to be made repeatedly to a server to get the latest update.

In this article we will see how the Fetch API can be used in conjunction with generators to build asynchronous APIs. The Fetch API is currently supported in Chrome, Opera, Firefox and Android browsers. We have a polyfill available from GitHub for unsupported browsers.

As ever, the code for this article can be found on our GitHub repository and there is a demo of the final technique at the bottom of the article.

Generators for Asynchronous Operations

Tip: If you need a refresher on what generators are and how they work, check out: ECMAScript 2015: Generators and Iterators

So how can we use generators to perform async operations? Well, if we analyze the way generators work we will find the answer.

A generator function implementing an iterator has the following structure:

function *myIterator(){
  while(condition){
    //calculate next value to return
    yield value;
  }
}

The yield keyword is responsible for returning a result and halting execution of the iterator function until it is next invoked. It also keeps the state of the function instead of rerunning everything when next you call it, effectively remembering the last place it left off.

We can re-imagine the above function without while loop as follows:

function *myIterator(){
  //calculate value 1
  yield value1;

  //calculate value 2
  yield value2;
  ...

  //calculate value n
  yield valuen;
}

The behavior of the function will be identical in both of the above cases. The only reason for using the yield keyword is to pause the execution of the function until the next iteration (which in itself seems kind of asynchronous). And as the yield statement can return any value, we can also return promises and make the function run multiple asynchronous calls.

Using Generators with the Fetch API

Tip: For a refresher on the Fetch API, check out: Introduction to the Fetch API

As mentioned earlier the Fetch API is intended to replace XMLHttpRequest. This new API provides control over every part of an HTTP request and returns a promise that either resolves or rejects based on the response from the server.

Long Polling

One of the use cases where the Fetch API and generators can be used together is long polling. Long polling is a technique in which a client keeps sending requests to a server until it gets a response. Generators can be used in such a case to keep yielding the responses until the response contains data.

To mimic long polling, I included an Express REST API in the sample code that responds with weather information of a city after five attempts. The following is the REST API:

var polls=0;

app.get('/api/currentWeather', function(request, response){
  console.log(polls, polls<5);
  if(polls < 5){
    console.log("sending...empty");
    polls++;
    response.send({});
  }
  else{
    console.log("sending...object");
    response.send({
      temperature: 25,
      sky: "Partly cloudy",
      humid: true
    });
    polls = 0;
  } 
});

Now, let’s write a generator function that calls this API multiple times and returns a promise on every iteration. Being on the client side, we don’t know after how many iterations we will get data from the server. So, this method will have an infinite loop pinging the server on every iteration and returning the promise on every occasion. Following is the implementation of this method:

function *pollForWeatherInfo(){
  while(true){
    yield fetch('/api/currentWeather',{
      method: 'get'
    }).then(function(d){
      var json = d.json();
      return json;
    });
  }
}

We need a function to keep calling this function and checking if the value exists after the promise resolves. It will be a recursive function that invokes the next iteration of the generator and only stops the process when it finds a value returned from the generator. The following snippet shows the implementation of this method and a statement that calls this method:

function runPolling(generator){
  if(!generator){
    generator = pollForWeatherInfo();
  }

  var p = generator.next();
  p.value.then(function(d){
    if(!d.temperature){
      runPolling(generator);
    } else {
      console.log(d);
    }
  });
}

runPolling();

As we see here, the first call to the function runPolling creates the generator object. The next method returns an object with a value property which in our case contains a promise returned by the fetch method. When this promise resolves, it will either contain an empty object (returned if the polls variable is below 5), or an object containing the desired information.

Next, we check for the temperature property of this object (which would indicate success). If it’s not present we pass the generator object back to the next function call (so as not to lose the state of the generator) or we print the value of the object to the console.

To see this in action, grab the code from our repo, install the dependencies, start the server, then navigate to http://localhost:8000. You should see the following results in the shell:

0 true
sending...empty
1 true
sending...empty
2 true
sending...empty
3 true
sending...empty
4 true
sending...empty
5 false
sending...object

And the object itself logged to the browser console.

Multiple Dependent Asynchronous Calls

Quite often, we need to implement multiple dependent asynchronous calls, where each successive asynchronous operation depends on the value returned by the the preceding asynchronous operation. If we have a group of such operations and they have to be called multiple times, we can put them together in a generator function and execute it whenever we need it.

To demonstrate this, I will be using GitHub’s API. This API provides us access to basic information on users, organizations and repos. We will use this API to get the list of contributors to a random repo of an organization and display the fetched data on the screen.

For this we need to make calls to three different endpoints. These are the tasks to be performed:

  • Get details of the organization
  • If the organization exists, get the organization’s repos
  • Get contributors to one of the organization’s repos (selected at random)

Let’s create a wrapper function around Fetch API to avoid repeating the code to create the headers and build the request object.

function wrapperOnFetch(url){
  var headers = new Headers();
  headers.append('Accept', 'application/vnd.github.v3+json');
  var request = new Request(url, {headers: headers});

  return fetch(request).then(function(res){
    return res.json();
  });
}

The following function consumes the above function and yields a promise for each invocation:

function* gitHubDetails(orgName) {
  var baseUrl = "https://api.github.com/orgs/";
  var url = baseUrl + orgName;

  var reposUrl = yield wrapperOnFetch(url);
  var repoFullName = yield wrapperOnFetch(reposUrl);
  yield wrapperOnFetch(`https://api.github.com/repos/${repoFullName}/contributors`);
}

Now, let’s write a piece of logic to call the above function to get the generator and then use the values obtained from the server to populate the UI. As every call to the generator’s next method returns a promise, we will have to chain these promises. The following is the skeleton of the code using the generator returned by the above function:

var generator = gitHubDetails("aspnet");

generator.next().value.then(function (userData) {
  //Update UI

  return generator.next(userData.repos_url).value.then(function (reposData) {
    return reposData;
  });
}).then(function (reposData) {
  //Update UI

  return generator.next(reposData[randomIndex].full_name).value.then(function (selectedRepoCommits) {
    //Update UI
 });
});

To see this in action, as detailed above, grab the code from our repo, install the dependencies, start the server, then navigate to http://localhost:8000. Or just check out the demo below (try rerunning it).

Demo

See the Pen Multiple Dependent Asynchronous Calls With the Fetch API by SitePoint (@SitePoint) on CodePen.

Conclusion

In this article I have demonstrated how the Fetch API can be used in conjunction with generators to build asynchronous APIs. ECMAScript 6 will bring a slew of new features to the language and looking for inventive ways to combine them and harness their power can often bring outstanding results. But what do you think? Is this a technique we can start using in our apps today? I would love to hear your thoughts in the comments.

Frequently Asked Questions (FAQs) about Asynchronous APIs using Fetch API and ES6 Generators

What is the Fetch API and how does it work with ES6 Generators?

The Fetch API is a modern, promise-based system for making network requests from the browser. It’s a built-in feature of modern browsers that provides a more powerful and flexible feature set compared to older solutions like XMLHttpRequest. ES6 Generators, on the other hand, are a new type of function in JavaScript that can be paused and resumed, allowing for more control over the flow of asynchronous operations. When used together, the Fetch API and ES6 Generators can provide a powerful tool for handling asynchronous data flow in a more readable and manageable way.

How can I handle errors when using the Fetch API with ES6 Generators?

Error handling with Fetch API and ES6 Generators can be done using the .catch() method on the promise returned by the fetch() function. This method is used to handle any errors that may occur during the fetch operation. Inside the .catch() method, you can define what should happen if an error occurs. This could be logging the error to the console, displaying an error message to the user, or any other appropriate action.

Can I use Fetch API and ES6 Generators with other JavaScript libraries or frameworks?

Yes, you can use Fetch API and ES6 Generators with other JavaScript libraries or frameworks. They are standard JavaScript features and can be used in any JavaScript environment. For example, you can use them in a React or Angular application, or with libraries like Redux or Vue.js.

What are the benefits of using Fetch API and ES6 Generators for asynchronous operations?

Using Fetch API and ES6 Generators for asynchronous operations can make your code more readable and easier to understand. Traditional callback-based asynchronous code can become difficult to read and manage as the complexity of your application increases. With Fetch API and ES6 Generators, you can write asynchronous code that looks and behaves like synchronous code, making it easier to reason about and debug.

Are there any limitations or drawbacks to using Fetch API and ES6 Generators?

While Fetch API and ES6 Generators provide a powerful tool for handling asynchronous operations, they do have some limitations. For example, Fetch API does not support canceling requests, and it cannot be used to make requests to another origin unless the server supports CORS. ES6 Generators, on the other hand, can be more difficult to understand and use correctly compared to traditional functions, especially for developers who are new to JavaScript or not familiar with the concept of generators.

How can I test my code that uses Fetch API and ES6 Generators?

Testing code that uses Fetch API and ES6 Generators can be done using standard JavaScript testing tools and techniques. For example, you can use a testing framework like Jest or Mocha to write unit tests for your functions. To test the fetch() function, you can use a library like nock to mock HTTP requests and responses.

Can I use Fetch API and ES6 Generators in a Node.js environment?

Yes, you can use Fetch API and ES6 Generators in a Node.js environment. However, since Fetch API is a browser-based API, it is not available in Node.js by default. To use it in Node.js, you will need to install a package like node-fetch or isomorphic-fetch that provides a Fetch API-compatible interface.

How can I debug my code that uses Fetch API and ES6 Generators?

Debugging code that uses Fetch API and ES6 Generators can be done using standard JavaScript debugging tools. Most modern browsers include a built-in JavaScript debugger that allows you to step through your code, inspect variables, and set breakpoints. You can also use console.log() statements to log information to the console, or use a tool like debugger; statement to pause execution of your code.

Can I use Fetch API and ES6 Generators in older browsers?

Fetch API and ES6 Generators are modern JavaScript features that are not supported in older browsers. However, you can use polyfills to provide equivalent functionality in older browsers. A polyfill is a piece of code that provides the functionality that you expect the browser to provide natively.

How can I improve the performance of my code that uses Fetch API and ES6 Generators?

There are several ways to improve the performance of your code that uses Fetch API and ES6 Generators. One way is to use the async/await syntax, which can make your code more readable and easier to understand. Another way is to use the Promise.all() function to execute multiple fetch operations in parallel. You can also use a library like lodash or async to provide additional utility functions for working with asynchronous operations.

Rabi Kiran (a.k.a. Ravi Kiran) is a developer working on Microsoft Technologies at Hyderabad. These days, he is spending his time on JavaScript frameworks like Angular JS, latest updates to JavaScript in ES6 and ES7, Web Components, Node.js and also on several Microsoft technologies including ASP.NET 5, SignalR and C#. He is an active blogger, an author at SitePoint and at DotNetCurry. He is rewarded with Microsoft MVP (ASP.NET/IIS) and DZone MVB awards for his contribution to the community.

asynchronous codees6Fetch APIgeneratorsiteratorsjameshlearn-modernjs
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week