I Promise Promises Are Promising
18 June 2014
Java Script “Callback Hell” TV Commercial:

Asynchronous call, not again! It is so difficult to handle errors! Why my code is so ugly? There must be a better way! … Search no more! The answer is in a software abstraction known as “promise”. If you learn it today, you get not one, but two libraries to solve your both server and client needs for price of learning one!
Let’s try to write some asynchronous code.

User must login in order to get data. Operations are asynchronous. To make it shorter, only first function is simulating asynchronous call using setTimeout, but you get the idea. Both functions could return errors:

  • 'Empty username'
  • 'User not authorized'
  • 'User has no data'

If everything is ok data will be dumped on the console. First let's do it in callback (hell) style.

var login = function (username, password, callback) {
if (username) {
// Simulate async by timeout
setTimeout(function () {
if (password === '1234') {
callback(true, null);
}
else {
callback(null, 'User not authorized');
}
}, 1000);
}
else {
callback(null, 'Empty username');
}
};
var getData = function (username, callback) {
if (username === 'vk') {
callback('Super important data.', null);
}
else {
callback(null, 'User has no data');
}
};

Let’s use our functions in callback style:

var username = 'vk';
var password = '1234';
login(username, password, function (success, error) {
if (success) {
getData(username, function (data, error) {
if (data) {
console.log(data);
}
else {
console.error(error);
}
})
}
else {
console.error(error);
}
})

First problem is error handling. Notice from code above that for each function in chain we have one error-handling block. This is super simple example and has only two functions. People from TV commercial had much more!

Second problem is using result from previous function. Second function must be nested in success callback of the first one.

Shortly, in JS-land there are gangs of asynchronous calls roaming main event loop. To put some order in it we can apply software abstraction called promise.

Key idea is simple. Our function will not call callbacks. It will return a promise. Promises are objects, and you can pass them around. Somewhere in the future promise will be:

  • Fulfilled by value
  • or
  • Rejected with error

Let’s create Login and GetData as promise.

var loginPromise = function (username, password) {
var deferred = Q.defer();
if (!username) {
deferred.reject('Empty username');
}
else {
setTimeout(function () {
if (password === '1234') {
deferred.resolve(); // You can pass some data here as in getDataPromise()
}
else {
deferred.reject('User not authorized');
}
}, 1000);
}
return deferred.promise;
};
var getDataPromise = function (username) {
var deferred = Q.defer();
if (username === 'vk') {
deferred.resolve('Super important data.');
}
else {
deferred.reject('User has no data');
}
return deferred.promise;
};

Now let’s use our promises:

var username = 'vk';
var password = '1234';
loginPromise(username, password)
.then(function () { return getDataPromise(username) })
.then(function (data) { console.log(data); }) // getDataPromise is returning data !
.fail(function (error) { console.error(error); })
.fin(function () { console.log('This will run always'); })

If any promise in the chain fails, promises after will be skipped and .fail will be executed.

We have one place .fail to handle errors from any function in a chain. We have .fin code segment that will be always executed. Looks familiar? try -> catch -> finally.

Promises are:

  • Clean
  • Offer uniform error handling (exception style error bubbling)
  • Easy to compose (parallel and sequential join)

There is much more in promises libraries. For example you can spin all promises at once using all(). In example below we are using fcall() to quickly create promise from value.

var promises = [];
promises.push(Q.fcall(function () {
console.log('First');
}));
promises.push(Q.fcall(function () {
console.log('Second');
}));
Q.all(promises)
.then(function () {
console.log('all done !')
})
.fin(function () {
console.log('the end');
})

So promises are great! They merge different callback conventions into call/return/exception pattern.

Recommended library is Q. Q has correctly implemented “then” function to return new promise instead of altering state of existing one. Q module can be loaded as script tag (client side). For the server side it is available in npm as “q” package. Also it is available from NuGet as “Q”.

Now you have no excuse to stop this madness and start using promises!