Taming Callbacks

Author: Shehroze Khan


Taming Callbacks

Give me a JavaScript function that says Hello after 2 seconds.

                        
    setTimeout(function() {
          alert(‘Hello’);

    }, 2000);

How are you? How’s Martha? How’s Jane? How’s Bob and the dog?

    
    setTimeout(function() {
        alert(‘Hello’);
        
        setTimeout(function() {
            alert(‘How are you?’);
      
            setTimeout(function() {
                alert(‘How’s Martha?’);
      
                setTimeout(function() {
                    alert(‘How’s Jane?’);
      
                    setTimeout(function() {
                        alert(‘How’s Bob?’);
      
                        setTimeout(function() {
                            alert(‘and the dog?’);
                        }, 2000);
                    }, 2000);
                }, 2000);
            }, 2000);
        }, 2000);
    }, 2000);

Time functions, Ajax requests, event handlers, and other asynchronous operations in JavaScript can lead to a nauseating nest of callback functions. It makes code hard to maintain and ugly to look at. Luckily, we have some options to get us out of callback hell.

**Asynchronous: the order functions are completed in is not guaranteed. **

Name your functions

Anonymous functions are convenient but also the source of the problem. Instead we can define our function before the callback:

    
    function sayHello() {
        alert(‘hello’);
    }
    setTimeout(sayHello, 2000);

When we start nesting deeper, naming your functions dramatically improves readability.

    
    function howAreYou() { 
      alert(‘How are you’);
    }
    function sayHello() {
        alert(‘Hello’);

        setTimeout(function() { 
        
            howAreYou
        }, 2000);

    }

    setTimeout(sayHello, 2000);

Named functions also make debugging easier since they appear more cleanly in the logs. However, this is not a complete solution: code can still get messy in more complex situations.

Make promises

Let’s imagine a bunch of friends work together to make some nachos. Vicky promises to bring the tortilla chips. Brittney takes the chips and promises to add cheese. Jasmine takes the cheesy chips and promises to bake them. Maria takes the melty goodness and promises to add some salsa. Let’s see this in code.

**Remember each step is asynchronous. We don’t want warm salsa or cold cheese. You can’t force your friends to do things, but we can make promises.**

A JavaScript promise is a paradigm we use to handle chains of callbacks. There many different implementations of promises, but they all follow the same basic structure. Let’s start with Vicky:

  
    //Load the library
    var Promise = require(‘Promise’);
    function vickyBringChips(storeHasChips) {
        
        //We are going to return a promise
        
        return new Promise(function(resolve, reject) {
          
            //Our exciting program at work.
          
            console.log(‘Vicky has brought the chips. Homemade greatness!’);
        
            //What if the store doesn’t have Chips?
            if(storeHasChips === false) {
            
                reject(); //This can take a parameter of any type
            }

            //Otherwise let’s fulfill our promise with chips, represented by my magnificent art.
          
            var chips = [‘>’, ‘>’, ‘>’, ‘>’];
          
            resolve(chips); //This can take a parameter of any type
      
        });

    }

The other steps will follow a similar pattern. But how do we get the chips? By using “then”

  
    vickyBringsChips(true).then(
        //The first function parameter is when our promise is resolved. 
        
        function(chips) {
        //chips will be the value passed into resolve when the Promise was defined.
        }, function(err) {
       
          //err will be undefined since we never passed a parameter when we called it.
        console.log(‘Oh no Vicky failed to fufill her promise. It was rejected’);
        }
    );

Brittney’s step will take chips as a parameter. After all, she needs them in order to add cheese.

  
    function brittneyAddsCheese(chips) {
      return new Promise(function(resolve, reject) {
        if (chips === undefined) {
           reject();
        }
        
        console.log(‘Added some cheese!’);
        
        //Those are some realistic looking chips and cheese
        
        var cheesyChips = [‘>||’, ‘>||’, ‘>||’, ‘ >||’]      
        
        resolve(cheesyChips);
      });
    }

At this point you might have figured out that Britney’s step can fit into the “then” function of Vicky’s step. We can then make Brittney’s “then” function takes Jasmine’s and Jasmine’s function can take Maria’s. This is promise chaining. Our function can either return the nachos or a promise that resolves nachos. In this case we will return a promise. Having some functions return promises and others return regular JavaScript types can be confusing for other developers. The code will look like:

  
    */
    function makeNachos(storeHasNachos) {
      //we will use this to handle all the potential promise rejection.
      
      var friendLetUsDown = function() { 
         return null;
      }

      //Remember “then” takes functions as parameters. 
      return vickyBringsChips ()
      
      .then(brittneyAddsCheese, friendLetUsDown)
      
      .then(brittneyAddsCheese, friendLetUsDown)
      
      .then(jasmineHeatsCheesyChips, friendLetUsDown)
      
      .then(mariaAddsSalsa, friendLetUsDown);

    }

It’s really important to remember NOT to add ‘;’ between items in the chain. Remember, each “then” is a function call of the previous functions’ return type, in this case a Promise.

Bluebird

Bluebird is a really cool promise library for JavaScript. With a ton of features, it could have its own article. Instead we are going to focus on one feature, promisify.

Let’s say you got a JavaScript function following the standard callback paradigm:

  
    function yourAmazingFunction(someObject, err, callBackFunc) {
     //All that brilliant code your wrote
     //Invoke the callback
     callBackFunc();
    }

Instead of rewriting this function to follow the promise paradigm, we can use bluebird to do it for us.

  
    var bluebird = require(‘bluebird’);
    
    var yourAmazingPromise = bluebird.promisify(yourAmazingFunction);

If someone forgets that yourAmazingPromise is a promise, they can still use it in the old callback manner.

I can make chips now. So what?

You could be an enterprise developer working on multi-million dollar projects with thousands of integrating systems or a startup developer on the brink of the next big thing or a vet who crawled his way through cobalt and into the cloud. Why should you spend your time on something so trivial?

More JavaScript projects exist on GitHub than any other language. UI’s are getting more complex than ever with single page applications using frameworks like ReactJS and Angular. The MEAN stack, MongoDB, express, Angular, and NodeJS, means we can write an entire web application only using JavaScript Npm is the biggest community of open source developers in the world built for one specific language.

JavaScript does not enforce formatting like Python. It’s dynamically typed, so it won’t complain like Java about passing around invalid types. It’s interpreted: no segmentation faults at compilation like C. If something’s broken, the user might be the first one to find out.

It’s up to us, the developers, to enforce good practices. Whether with teammates, the open source community, or your friends, we are going to write code together. Endless callbacks hurt our ability to cooperate. Let’s not make hell other people.