JT in Helsinki

Home About RSS

Promisifying your XMLHttpRequest

  • code
  • javascript
  • promise

I come from a Java background and when working with callbacks I often feel they are cumbersome. Working with promises feels far more logical for me. Callbacks are messy and they have a nasty side effect of obfuscating code. Promises on the other hand provide a way of creating a far cleaner code flow by making code look more like synchronous code.

If you’re not using the Fetch API then let’s take a web request using XMLHttpRequest. Typically we setup out XMLHttpRequest object and handle the response with callback functions. It’s often difficult to know when those

[gist]beb92d02ccea508e4f549d28fe2b3373[/gist]

Now calling the function is simply a matter of calling it together with the appropriate callbacks.

    // call the requestFunction() by passing in 
    // a success and error callback. 
    request('http://localhost/user/:id', (response) => { 
        // do something 
    }, 
    (error) => { 
        // do something 
    });

However, we sometimes need to make multiple requests on a page. especially when the application becomes more complex. Because of the asynchronous nature ofXMLHttpRequest we need to nest the second call to the http://localhost/user/:id/history endpoint. That’s not idea as it makes our code difficult to follow.

    // call the requestFunction() by passing in 
    // a success and error callback. 
    request('http://localhost/user/:id', (response) => { 
        let user = JSON.parse(response.body);
        // nest this call.
        request(`http://localhost/user/${user.departmentId}/history`, 
        (response) => { 
            // do something 
        }, 
        (error) => { 
        // do something 
        }); 
    }, 
    (error) => { 
        // do something 
    });

In contrast, promisifying the function means we can execute our code multiple times whilst waiting for the previous function to finish.

First, promisifying the XMLHttpRequest object is done by wrapping the XMLHttpRequest inside a promise object. We provide two callbacks. success and reject.

By using the async keyword we are marking this function as asynchronous. When an async function is called, it returns a Promise which returns a value to be resolved in the calling code.  When an exception is thrown the Promise will be rejected with the thrown value.

[gist]c2839a39da3ec80a90b1c7cb278d90a2[/gist]

Calling our newly promisifiied function can be done in two ways. The first way is a bit easier to read but to be honest, I think it’s not too different to the first method presented with callbacks. However it does give a cleaner flow.

    makeRequest('http://localhost/user/:id') 
    .then((user) => {
        // do something
    }
    .catch((error) => {
        // do something
    });

However we are not far from where we started when it comes time to adding a nested request.

    makeRequest('http://localhost/user/:id') 
    .then((user) => {
        // nested call
        makeRequest(<code>`http://localhost/user/${user.departmentId}/history</code>`) .then(userHistory => { console.log(userHistory); }) .catch((error) => { console.log(error); }); }) .catch((error) => { console.log(error); });

Thankfully there is a far cleaner, clearer and easier way. By using the await keyword we do away with the nested calls, the callbacks and the mess. Inside a function marked with the async keyword we can call themakeRequest() function using the await keyword like so:

    let user = await makeRequest('http://localhost/user/:id'); 
    let userHistory = await makeRequest(<code>`http://localhost/user/${user.departmentId}/history</code>`);

The await keyword pauses the execution of the async function and waits for the passed Promise’s resolution and then resumes the async function’s execution and returns the resolved value. You can’t use the await keyword outside of an async function. The purpose of async/await functions is to simplify the behaviour of using promises synchronously and to perform some behaviour on a group of Promises.

So there you have it. A simply way to quickly improve code readability (and debugability) through employing a promise and the asyncawait keywords. The (preferable) other alternative is to use the Fetch API which is promisified straight out of the box. Nice!