What are callbacks and promises?

To explain promises we must first explain callbacks. A callback function is simply a function you pass into another function so that function can call it at a later time. This is commonly seen in asynchronous APIs; the API call returns immediately because it is asynchronous, so you pass a function into it that the API can call when it's done performing its asynchronous task.

Callbacks

The simplest example I can think of in JavaScript is the setTimeout() function. It's a global function that accepts two arguments. The first argument is the callback function and the second argument is a delay in milliseconds. The function is designed to wait the appropriate amount of time, then invoke your callback function.

setTimeout(function () {  
    console.log("10 seconds later...");
}, 10000);

You may have seen the above code before but just didn't realize the function you were passing in was called a callback function. We could rewrite the code above to make it more obvious.

var callback = function () {  
    console.log("10 seconds later...");
};
setTimeout(callback, 10000);  

Callbacks are used all over the place in Node because Node is built from the ground up to be asynchronous in everything that it does. Even when talking to the file system. That's why a ton of the internal Node APIs accept callback functions as arguments rather than returning data you can assign to a variable. Instead it will invoke your callback function, passing the data you wanted as an argument. For example, you could use Node's fs library to read a file. The fs module exposes two unique API functions: readFile and readFileSync.

The readFile function is asynchronous while readFileSync is obviously not. You can see that they intend you to use the async calls whenever possible since they called them readFile and readFileSync instead of readFile and readFileAsync. Here is an example of using both functions.

Synchronous:

var data = fs.readFileSync('test.txt');  
console.log(data);  

The code above blocks thread execution until all the contents of test.txt are read into memory and stored in the variable data. In node this is considered very bad practice.

Asynchronous (with callback):

var callback = function (err, data) {  
  if (err) return console.error(err);
  console.log(data);
};
fs.readFile('test.txt', callback);  

First we create a callback function that accepts two arguments err and data. One problem with asynchronous functions is that it becomes more difficult to trap errors so a lot of callback-style APIs pass errors as the first argument to the callback function. It is best practice to check if err has a value before you do anything else. If so, stop execution of the callback and log the error.

Synchronous calls have an advantage when there are thrown exceptions because you can simply catch them with a try/catch block.

try {  
    var data = fs.readFileSync('test.txt');
    console.log(data);
} catch (err) {
    console.error(err);
}

In asynchronous functions it doesn't work that way. The API call returns immediately so there is nothing to catch with the try/catch. Proper asynchronous APIs that use callbacks will always catch their own errors and then pass those errors into the callback where you can handle it as you see fit.

Promises

In addition to callbacks though, there is another popular style of API that is commonly used called the promise. Promises are a style of programming that allows us to flatten out asynchronous APIs so there aren't so many callbacks and nested callbacks. I think the best way to understand promises is to see them in action and then talk about them.

Let's say we want to load a file from the file system, then make a database call, then send an email. All of these things have asynchronous API calls and would look something like this:

fs.readFile('test.txt', function (err, data) {  
    if (err) return console.error(err);
    db.insert({
        fileName: 'test.txt',
        fileContent: data
    }, function (err, result) {
        if (err) return console.error(err);
        smtp.send({
            to: 'test@test.com',
            subject: 'test',
            body: 'This is a test.'
        }, function (err) {
            if (err) return console.error(err);
            console.log("Success email sent.");
        });
    });
});

This is what most people call "callback hell". It's hard to read and difficult to maintain. We could flatten this out a little with named callbacks:

var emailSent = function (err) {  
    if (err) return console.error(err);
    console.log("Success email sent.");
};
var fileSaved = function (err, result) {  
    if (err) return console.error(err);
    smtp.send({
        to: 'test@test.com',
        subject: 'test',
        body: 'This is a test.'
    }, emailSent);
};
var readFileDone = function (err, data) {  
    if (err) return console.error(err);
    db.insert({
        fileName: 'test.txt',
        fileContent: data
    }, fileSaved);
};
fs.readFile('test.txt', readFileDone);  

It does reduce some of the nesting issues but I honestly don't think that is all that much easier to understand. We may be able to clean it up a bit more, even declare the functions in a more comprehensive order for readability. No matter what we do to clean it up a bit though, it is still a complicated mess. You have to check for errors every step of the way and if you want to avoid callback hell then you have to stop and brainstorm about an assortment of functions to define that you can use as your callbacks.

Let's do the same thing with promises shall we? We'll pretend that fs.readFile, db.insert, and smtp.send all return promises.

fs.readFile('test.txt')  
    .then(function (data) {
        return db.insert({
            fileName: 'test.txt',
            fileContent: data
        });
    })
    .then(function () {
        return smtp.send({
            to: 'test@test.com',
            subject: 'test',
            body: 'This is a test.'
        });
    })
    .then(function () {
        console.log("Success email sent.");
    }, function (err) {
        console.error(err);
    });

Without knowing how this magic is working it should still be pretty obvious how much better this is. The first and most obvious reason is that you no longer have callback hell, but as Domenic Denicola pointed out in his blog post, flattening out callbacks is just a happy side effect of promises. Promises do a lot more than make our code easier to read and organize; they also give us back control over our error handling. Did you notice in the above example that we only check for errors one single time?

A promise is simply an object that exposes a .then function that takes two callbacks as arguments. The first callback is the done or success callback. The second callback is the fail or error callback. The nifty part about promises is that you only have to specify an error callback on the very last call to .then. If an error is thrown it will be passed up the chain to the final error handler. Promises essentially give us back the error handling that was robbed from us when we decided to embrace asynchronous code.

In the previous example we pretended that those functions were already coded to return promises; Now let's say those functions actually do use callbacks. In an effort to understand promises better let's find a promise library and use it to turn those callback APIs into promise APIs. There are lots of promise libraries already out there. One of the most popular is called Q. We can use Q to turn those callback-style functions into functions that use promises.

// promiseWrappers.js

var Q = require('q'),  
    fs = require('fs'),
    db = require('./db'),
    smtp = require('./smtp');

exports.fs = {  
    readFile: function (fileName) {
        var deferred = Q.defer();
        fs.readFile(fileName, function (err, data) {
            if (err) return deferred.reject(new Error(err));
            deferred.resolve(data);
        });
        return deferred.promise;
    }
};

exports.db = {  
    insert: function (doc) {
        var deferred = Q.defer();
        db.insert(doc, function (err, result) {
            if (err) return deferred.reject(new Error(err));
            deferred.resolve(result);
        });
        return deferred.promise;
    }
};

exports.smtp = {  
    send: function (msg) {
        var deferred = Q.defer();
        smtp.send(msg, function (err) {
            if (err) return deferred.reject(new Error(err));
            deferred.resolve();
        });
        return deferred.promise;
    }
};

We just created a module that exposes some promise-enabled wrappers around some APIs that do not understand promises, but use callbacks instead. Now we can use the promise-style script we looked at before and it will work as intended :D

var p = require('./promiseWrappers');

p.fs.readFile('test.txt')  
    .then(function (data) {
        return p.db.insert({
            fileName: 'test.txt',
            fileContent: data
        });
    })
    .then(function () {
        return p.smtp.send({
            to: 'test@test.com',
            subject: 'test',
            body: 'This is a test.'
        });
    })
    .then(function () {
        console.log("Success email sent.");
    }, function (err) {
        console.error(err);
    });

We created our wrappers for learning purposes, but if you've never used Q before then you'll be excited to find that Q actually has helper functions for wrapping node-style err, result API calls. We don't even need to write our promiseWrappers.js file. We can just modify our code to use Q.

Q.nfcall(fs.readFile, 'test.txt')  
    .then(function (data) {
        return Q.nmcall(db, 'insert', {
            fileName: 'test.txt',
            fileContent: data
        });
    })
    .then(function () {
        return Q.nmcall(smtp, 'send', {
            to: 'test@test.com',
            subject: 'test',
            body: 'This is a test.'
        });
    })
    .then(function () {
        console.log("Success email sent.");
    }, function (err) {
        console.error(err);
    });

If your function is just a function but uses the node-style callback with an err, result signature then use Q.nfcall to turn it into a promise-style call. If your function is a method that uses the node-style callback then in order to preserve the context for the this keyword within the method you should use Q.nmcall which takes two arguments (the object and the method name) plus any that you intend to pass as arguments to the function.

Promises are even more fun when you have a bunch of asynchronous activities and none of them depend on the result of another. It means you can run them all in parallel and then just run some code when they are all finished. Most promise libraries expose helpers that allow you to do this easily. Even Q does this.