Telerik blogs
JavascriptD_870

We'll go through the basics of Promises and callbacks, and demonstrate the simplicity of promisifying the old XMLHttpRequest API.

It is no longer news that new JavaScript development patterns since the evolution of ES6 have influenced how we approach certain things in the JavaScript ecosystem. For instance, the standard callback function syntax which has been a standard for years. However, a new alternative, Promises, is now a more preferred pattern of development.

Promises

Promises represent a common way to react to asynchronous operations in JavaScript. A good example of a Promise-based API is the new fetch API provided by browsers. Not that you don’t already know this, but fetch is a replacement for the older callback-based XMLHttpRequest API, and, in this post, we are going to look at a few ways we can promisify some of our older APIs just for fun. 😃

Callbacks

Let’s start with simple callbacks. In my opinion, the most common browser API that we come across is the setTimeout() API. It waits for a specified period of time and then executes a callback. A typical demonstration is this:

window.setTimeout(() => {
    
// do something after 1sec
  
}, 1000);

As expected, the function waits for a second and then executes. With that in mind, let’s go ahead and see how we can promisify it just to understand how Promises better clarifies asynchronous processes.

Promisify a Callback

To define an A+ compliant Promise using the Promise constructor, we will pass in a function to the constructor. The function requires two arguments, resolve and reject, which are themselves callback functions under the hood. This is what we mean:

const promisifiedSetTimeout = (delay, value) =>
    
new Promise((resolve) => window.setTimeout(resolve, delay, value));

Notice that we did not use the reject callback here. Why? Because setTimeout() does not provide any hooks for an error state. So instead, we pass resolve as the callback to setTimeout().

This gives us a chainable setTimeout() function. As much fun as it is to do stuff like this (promisify callbacks), I will advise you don’t get carried away. Many JavaScript APIs and most standard browser APIs are still heavily callback driven. This includes popular libraries like jQuery.

As a developer, this brings us to a bit of a crossroads, where we are caught between mixing two different styles of asynchronous code.

XMLHttpRequest

But who says we can’t just convert all our callbacks to promises? Let’s look at a more complex example and promisify the popular XMLHttpRequest API. But before that, let’s demonstrate the functionality to understand it better.

The XMLHttpRequest API was designed to fetch XML data over HTTP, hence the name. But today it can be used with protocols other than HTTP and it can fetch data not only in the form of XML, but also JSON, HTML or plain text. Here’s a quick example using the XMLHTTPRequest to fetch a Github user:

function success(){
var data = JSON.parse(this.response);
console.log(data);
}
  
function error(err){
console.log('You have an error', err)
}
  
var request = new XMLHttpRequest();
request.onload = success;
request.onerror = error;
request.open('GET', 'https://api.github.com/users/kennypee');
request.send();

Here we have defined our request that we’ll use to retrieve data from the server asynchronously. The request returns a response from the server and stores it in the response variable. We also converted the response into a JavaScript object using the JSON.parse() method. In addition, we have defined two listeners to handle the success and error cases.

The progression here is, first, we open the request by passing the request method GET and the request URL where we’ll fetch the data from, then we send the request with the call to send().

If our request is successful, we store the data from the server into our response variable and convert it into an object and then log it to the console. If there is an error in our request, we log the error to the console with the error() case handler.

This brings us back to our initial question, is there a cleaner and simpler way to do this? The answer is YES. We can use Fetch to simplify this process in modern JavaScript development. Fetch is an improvement on the XMLHttpRequest API. The major difference between Fetch and XMLHttpRequest is that the Fetch API uses Promises, hence avoiding callback hell.

Fetch

The fetch() function takes a compulsory argument, which is the path to the resource you want to fetch. The functions return a Promise in any case, whether success or error. It has dedicated handlers for either case. If the request is successful, the .then() function will receive the response object from the server; if the request fails, the .catch() function receives the error object.

If we wanted to achieve the same functionality we have on the previous example with Fetch, here’s what we’ll do:

fetch('https://api.github.com/users/kennypee')
.then(function(response)){
return response.json()
})
.then(function(data)){
console.log(data)
})
.catch(function(err)){
console.log('You have an error', err)
});

Here we have done the same thing we did with the XMLHttpRequest but in a much simpler way. We’ve made use of the Fetch API to call to GitHub endpoint and fetch data about a user. When the promise is resolved, we get a Response object in return.

But the response object here only has information about the response itself and not of the user we asked for. To get the data we desire, we had to pull it from the response body by calling the .json() method on the response object to return a Promise, hence the need to chain another .then() function for the data.

Promisify the XMLHttpRequest API

Now that we have seen an undiluted XMLHttpRequest API and demonstrated the Fetch version of the same API, let’s try to promisify the XMLHttpRequest API. This will be more of a mix of both the XMLHttpRequest API and Fetch. To promisify our previous example of the XMLHttpRequest API, here are the steps we’ll take:

  1. Remove the success and error arguments in the function signature.
  2. Modify the function body to return a Promise.
  3. Instead of calling success(), resolve the Promise.
  4. Instead of calling error(), reject the Promise.

With these modifications, we can promisify our XMLHTTPRequest API to get the result below:

const fetch = (url, options = {method:'get'}) => new Promise((resolve, reject) => {
    let request = new XMLHttpRequest();  
    request.onload = resolve
    request.onerror = reject;
    request.open(options.method, url, true);  
    request.send();
});

This is a concise way of promisifying the XMLHttpRequest API without having to rewrite the existing code. More so, we can always replace all the old callback functions with the new .then() and .catch() syntax wherever the need be.

Extras

It might also interest you to know that, since Node.js version 8, you can convert all your callbacks to Promises using a built-in module called the util. All you need to do is call util.promisify() to convert your callback to a Promise.

Conclusion

Not that you don’t know all these but I hope you had fun doing it all over again to reinforce the knowledge. We demonstrated the conversion of usual callbacks to Promises and more popular XMLHttpRequest to Fetch just for the fun of it. To learn more about Promises, feel free to check out this documentation.

For More Info on Building Great Web Apps

Want to learn more about creating great web apps? It all starts out with Kendo UI - the complete UI component library that allows you to quickly build high-quality, responsive apps. It includes everything you need, from grids and charts to dropdowns and gauges.

Learn More about Kendo UI

Get a Free Trial of Kendo UI



About the Author

Christian Nwamba

Chris Nwamba is a Senior Developer Advocate at AWS focusing on AWS Amplify. He is also a teacher with years of experience building products and communities.

Comments

Comments are disabled in preview mode.