Be the first user to complete this post

  • 0
Add to List

JavaScript Promises - Visualized

When you work with javascript, you will often find that callbacks are all over the place. They are in your front end code, in your middleware, and in case you use it to interface with your database, you would have it in your back end code too. Heck, they even come to haunt you in your dreams. One of the cleverest ways to avoid this hell is by using promises. Promises are a relatively new concept in javascript require you to think about your code in a slightly different, yet interesting way.

I recently had the opportunity to mess around with promises a lot. In this post, I will just touch upon some of ways I ended up using promises and provide those snippets for quick reuse. Look at the following visualization now, and then look at it again at the end of this post. It explains how the functions are associated when you create new promise obejcts.

Promises Visualized

I am not going to go into detail of what promises are and its different use cases. You can always read this amazingly long article on html5rocks for that. However, if you dont have the time, this article should at least help you to address the matter at hand. So, how do you create a promise?
var getData = new Promise(function(resolve, reject){

    // Do something asynchronous and pass it a callback
    getDataFromAMillionMilesAway(function(data){

        if(data){
            resolve(data);
        }
        else {
            reject(new Error("Thats too far away"));
        }

    });

});

// And here's how you'd use it

getData().then(function(data){
    console.log('Got Some Data! ', data);
});
As you can see above, the first function passed to the then() function receives the value that you pass to the resolve() function. So what happens to the error case? Well, we didnt capture it in the above example. But just like the promise constructor, the then function can take two functions are arguments. The first function is invoked upon calling resolve, and the second one is invoked upon calling reject with the corresponding parameters.
getData().then(
    function(data){
        console.log('Got Some Data! ', data);
    },
    function(error){
        console.log('Something really bad happened :( ', data);
    }
);
So that takes care of the problem of functions. You might wonder what good is the promise if you need to call the getDataFromAMillionMilesAway function with a parameter. Well, closures to the rescue.
var getData = function(config){

    return new Promise(function(resolve, reject){

        // Do something asynchronous and pass it a callback
        getDataFromAMillionMilesAway(config, function(data){

            if(data){
                resolve(data);
            }
            else {
                reject(new Error("Thats too far away"));
            }

        });

    });

}
Note that in this case, we are returning the new Promise from the getData function. This effectively makes getData a promise which retains the value of the ‘config’ in a closure using which you can call the then function just like before. One might argue that this is just a sugarcoat on callbacks because as you can see, we are not avoiding callbacks altogether. In fact, they are they most important part that lead to either the resolution or rejection of a promise. However, by encapsulating the callback logic in a single place you can now write much cleaner code like this.
getData(config1)
  .then(function () {
    return getData(config2);
  })
  .then(function ()  {
    return getData(config3);
  })
  .then(function(){
    console.log('All the data was fetched sequentially.');
  });
That looks much more like plain English, doesn’t it? You might have just delayed your inevitable baldness by a few years. And that is why promises are so awesome. They save your hair. The other important thing about promises is that you can return a value from a function to resolve a promise with that value. For example, the following code works perfectly fine
getData(config).then(function(data){
     data = _.extend({}, data, {'key','valye'});
     return data;
}).then(function(modifiedData){
    console.log(modifiedData);
});
Since all you need is to return a value to resolve a promise, any function that returns a single value can be used as an argument to the then() function. E.g. Here's what I mean
getData(config)
    .then(function(data){
        return JSON.parse(data);
    })
    .then(function(parsedData){
        console.log(parsedData);
    });

//is equivalent to

getData(config)
    .then(JSON.parse)
        .then(function(parsedData){
            console.log(parsedData);
    });
Once you get a taste of it, I promise you, you will not want to call back ;)



Also Read:

  1. es6 iterators and iterables - creating custom iterators
  2. box-sizing
  3. Difference between async and defer attributes in script tags
  4. Getting the parameters and arguments of a javascript function
  5. Generators and Yield in es6