Let’s behold the mechanical miracle of a Promise, and see how easy it actually is (and undeniably satisfactory) to manually write an interface with similar syntactic sugar, without using Promise().

Let’s say we’d want to create a random number generator.

https://xkcd.com/221/

And, we’d also like to know if it’s odd or even. Here’s the syntax we were promised (pun intended):

getRandomNumber(1, 10)
  .thatsOdd(function(number) {
    alert(number + ' is odd!')
  })
  .thatsEven(function(number) {
    alert(number + ' is even!')
  })

Sweet. Now, how would one go about creating that getRandomNumber function?

To make it compile, this is the structure we need to start with:

function getRandomNumber(min, max) {
  
  // return the object that exposes our two methods
  return {
    thatsOdd:  function(h) {
      // let each method return this, to allow chaining
      return this;
    },
    thatsEven: function(h) { 
      // let each method return this, to allow chaining
      return this;
    }
  };
}

The scope that is being returned offers our Promise’s API. It contains two methods thatsOdd and thatsEven. Both methods receive the user’s handler that processes that particular situation.

To make this work we need to asynchronously call either the user-defined h handler that they passed in thatsOdd or thatsEven. Here’s how:

function getRandomNumber(min, max) {
  
  // generate the number and see if it's even
  var number = Math.floor(Math.random() * max) + min; 
  var isEven = number % 2 == 0;

  // define the final handler
  var finalHandler;

  // async call to allow caller to map either handler
  setTimeout(function() {
    finalHandler(number);
  }, 0)

  // return the object that exposes our two methods
  return {
    thatsOdd:  function(h) {
      // map the odd handler that will be called async
      if (!isEven) finalHandler = h;
      // let each method return this, to allow chaining
      return this;
    },
    thatsEven: function(h) { 
      // map the even handler that will be called async
      if (isEven) finalHandler = h;
      // let each method return this, to allow chaining
      return this;
    }
  };
}

That’s all there is to it! Here’s a jsfiddle.

Of course there are many optimizations that can be done, but this is the gist that hopefully explains a bit about how to build an async chainable API manually without clutter.

Now go have an awesome day behind your computer.


Check out our Engineering Blog for more in depth stories about pragmatic code for happy users!