Wednesday, April 6, 2016

Callback hell and promises


Callback? What the hell is callback? Well in a nutshell, call back means:

Now this is what just the English meaning of call back. This in turn means executing something when other thing is finished and this in turn is not something that keeps on waiting for the thing to finish, rather keeps on doing its other stuff and executes when the thing is finished on an event basis.

1. What is callback?

The statement above is a bit lengthy in a way to understand, but think of this that when you give exams in school you never waits for the result( although you do :D ), rather you start preparing for the next class and when the result arrives, go to school to collect it. So if we have to think this in a code manner it would look something like this:

giveExams(getResult(){
     //when results arrive
})


This pattern is very common in Javascript world. If you ever been worked with Javascript in client side ( or server side, since Node js made that possible ) you would have faced the situation when you have to get some data from the server without doing the full reload of the page using ajax call and then have to display the result on the page at some specific div or text area.

This can be done using callback in Javascript like this:

$.ajax({
     url : "", //where to hit

     type : "POST",//how to hit, POST, GET, DELETE, PUT
     data : {},//what to send
     success : function(resposne){//in case of success
          //this executes when call gets successful and we have the response in the response object of the function paramter
     },

     error : function(error){//in case of failure
          //this executes when call faces some error

     }
});


FYI : The call above uses Jquery framework to call the ajax function.

The function named success and error are nothing but the callbacks, since they are going to be called once the main call comes back either with the failure or with the success status.

2. What is callback hell?

The way callback is handled a pretty different then most of the other languages handle the stuff. For instance in Java:
public static void sleepAndPrint() throws Exception{
     System.out.println("I am before sleep");
     Thread.currentThread().sleep(2000);
     System.out.println("I am after sleep");
}

Calling the above function will provide you the output as:

I am before sleep. (There will be a pause of 2 seconds and then next statement occurs)
I an after sleep.

Now considering the same and making it in Javascript we have something like this:
function sleepAndPrint(){
     console.log("I am before timeout");
     setTimeout(function(){
          console.log("I am in timeout");
     }, 2000);
     console.log("I am after timeout");
}

Now when you run this you will get some thing like this:

I am before timeout.
I am after timeout.
I am in timeout.

If you see at the time of timeout function the code didn't stopped and keeps on executing so the statement that was inside the timeout block actually got executed after the whole program gets done.

This is good in a way that Javascript actually follows Non-Blocking I/O model, but it also creates a problem for those who wants to do the stuff in a sequential basis, like you want to get the data of a user from the database, that will contain the github id of the user from which you can query the github servers using the github api and get the avatar url of the user so that you can download it in a temporary folder. Now the above mentioned sequence is a straight forward one if we have to do it in Java like this:
public User getData(final int userId) {
     //this will be having the code to access the db layer and get the User model out of it
     return user;
}

public String getUserAvatarUrlFromUser(final int userId){
     User user = getData(userId);
     String avatarUrl = user.getAvatarUrl();
     return avatarUrl;
}

public Image getImageFromGihubForAvatarUrl(final int userId){
     String avatarUrl = getUserAvatarUrlFromUser(userId);
     Image image = github call to get the image of the avatar url just found;
     return image;
}
//Some where in the code
getImageFromGihubForAvatarUrl(1);

This will go on the way we have planned and the code when talking to database actually waits for the operation to complete and in the same way while talking to the github servers actually waits which makes our program to run in sequence and thus does not make any problem for us.

But in case of Javascript since the default behavior is different this sequence is not going to work properly.

So we have to do work like this, not exactly but something like this:
var getUserAvatarWithCallback = function(userId, callback){
  db.getUsers("userId", function(error, response){
    if(error){callback(error, null);}
    else{
      var userIdOfGh = response.user.githubId;
      github.search.users({ q : userIdOfGh }, function(err, res){
        if(err){ callback(err, null); }
        else{
          var avatarUrl = res.items[0].avatar_url;
          callback(null, avatarUrl);
        }
      });
    }
  })
};

Using this will be something like this:
getUserAvatarWithCallback("userIdToFind", function(error, response){
     if(error){console.error("Error", error);}
     else{
          console.log("Found the user, url is ", response);
     }
});

Now can you imagine how the indentation is going inward and inward since in order to do the stuff in a sequential manner we have to keep on making the calls inside the callbacks again and again, that makes the stuff pretty ugly. This is termed as the CALLBACK HELL or PYRAMID OF DOOM.

3. What is a Promise?

A Promise represents a proxy for a value not necessarily known when the promise is created. via Mozilla, here

Now if you go through the definition you find out it says that it is a proxy whose value is not known or available at the time of creation of the promise. So when we make promise we just code the way we do it in other languages.

Let us take an example of Promise using the above example only:
var getUserGithubIdFromDb = function(userId){
     return (new Promise(function(resolve, reject){
          db.getUsers(userId, function(error, response){
               if(error){reject(error);}//(A)
               else{
                    resolve(response.user.githubId);//(B)
               }
          });
     }));
};
var getAvatarUrlFromGithub = function(githubUserId){
     return(new Promise(function (resolve, reject) {        
          github.search.users({q: githubUserId}, function(error, response){
               if(error){reject(error);}
               else{
                    resolve(response.items[0].avatar_url);
               }
          });
     }));
};

The code above is going to be used as follows:
getUserGithubIdFromDb("userIdToFind")
     .then(function(response){//(D)
          getAvatarUrlFromGithub(response);
     })
     .catch(function(error){//(C)
          console.log("error", error);
     });

If you see this code is pretty maintainable and also is continuous in nature meaning it will be chained to more promises the way we did for 2 functions.

The main thing to note is that at equation (A) we called the reject(error), where reject is the parameter in the Promise constructor which will stop the further execution of the function and throw the error there itself which is going to be caught at (C), and if you are successful in your execution then you have to call resolve(response) [please see equation(B)], where response is passed as a parameter to equation (D), where we have passed it as a parameter to my further processing. This pattern is continuous in nature and you can chain as much promises as you want.


No comments:

Post a Comment