PromisifyAll!

Javascript

I found an awesome Javascript method the other day that I wanted to quickly share. It's called promisifyAll and it's part of the Bluebird library. I'll give a little more context on promises and why they are useful and then provide info and an example on how to get started using promisfyAll below.


I have been writing quite a bit of asynchronous server code recently and consequently have been dealing with the joys of callbacks (sarcasm). Callbacks are a necessary evil when writing async functions because they allow your server to continue performing other operations before coming back to execute your callback once the async process completes running.

Having a few callbacks is certainly manageable, but as you create callbacks nested within callback, you quickly descend into callback hell:

asyncFunction(function() {
  // do something and call another async function
  return moreAsync(function() {
    // more logic followed by another async method
    return evenMoreAsync(function() {
      // Callback hell!
    });
  });
});

Nested callbacks can quickly create ugly pyramid code that is hard to read and even harder to extract information out of. Imagine you want the parent function to return the value derived from the innermost callback. You would have to pass that value to each containing function's callback until it reached the parent. I reiterate: Ugly!

Enter promises. Promises are quickly growing in popularity and will come packaged with the upcoming release of ECMAScript 6. Promises serve as an abstraction layer on top of callbacks. Promises turn callbacks into chainable methods that accept functions as arguments and pass them the value returned from the async call. Essentially promises make asynchronous operations behave in a synchronous manner and improve code readablility. Here's that above function using promises:

return asyncFunction()
  .then(function() {
    return moreAsync();
  })
  .then(function() {
    return evenMoreAsync();
  });

Going further, promises allow adding one method at the end of your chain which will catch any errors encountered along the promise chain. Quite a bit more convenient than having to handle errors at each callback (although you can do that if you like).


As mentioned above, I've really enjoyed working with the Bluebird promise library. Bluebird comes with all the standard promise features, but also has some amazing advanced tools including promisifyAll.

promisifyAll is a game changer that powers almost any library with the ability to use promises instantly. How do you use it? It's incredibly simple:

  1. Include bluebird
  2. Call promisifyAll on a library you'd like to add promises to.
  3. Start using promises. What? Yeah I know right!

PromisifyAll will loop through the target library and append 'Async' to each method in the library. These new methods return promises and can be chained.

Here's some example code for extending MongoDB. In this code we'll simply look up a user, modify their name and save the updated user. The code uses an async query and save function. I'll write the example first using callbacks and then with the new Async promise methods:

var Promise = require('bluebird');
var db = require('mongodb');
Promise.promisifyAll(db);

// Basic function using callbacks.
var updateEmailWithCallbacks = function(oldEmail, newEmail) {
  db.users.findOne({ email: oldEmail }, function (err, user) {
    // Need error handling for each async operation.
    if (err) return { error: err };
    else {
      user.email = newEmail;
      user.save(function(err, updatedUser) {
        if (err) return { error: err };
        else {
          return updatedUser;
        }
      })
    }
  });
});

// Promisified function using promise chain.
var updateEmailWithPromises = function(oldEmail, newEmail) {
  return db.users.findOneAsync({ email: oldEmail })
    .then(function(user) {
      user.email = newEmail;
      return user.saveAsync();
    })
    .then(function(updatedUser) {
      return updatedUser;
    })
    .catch(function(err) {
      // Handles any error encountered in the promise chain.
      return { error: err };
    });

Hope you enjoyed this brief introduction to promises and promisifyAll. I only scratched the surface on what promises can do, but they are awesome. Bluebird has an amazing repo chocked full with examples, so head over there if you'd like to dive deeper into their docs.