JavaScript promises

explanation javascript pouchdb

This is just a basic introduction to javascript promises. This is inspired by this article, which explains the intricacies very well.

javascript callbacks

One of the unique feature of javascript is the use of callback functions. Its extremely essential considering the fact that javascript is a single threaded language.

For those that have no idea what I am talking about, here is how callback functions are usually used.

function getData(callback){
    callback(loadDataFromSomewhere());
}

getData(processData);

When the getData is done, the result is passed into processData.

Callbacks are all well and good, until you start working on some complicated API project and all hell break loose. If you have been working with callbacks for a while now, you would probably have heard the term “pyramid of doom”, where your function calls stretches all the way to the right.

But there are more to [promises] than nice looking code. Using callbacks deprives us of basic programming features such as return and throw. More importantly, when something is wrong, there is no way to track down where that mistake is. All these results in bad code design.

Javascript promises

Promise, as defined by the A+ spec, is like an upgraded version of callback functions.

For most modern browsers, promises are implemented as window.Promise, but for those who are looking good polyfill, is one option you can look at.

Basic functions (then & catch)

If you use promises, there is two things you cannot do without, the functions then and catch. This knowledge should be sufficient to use most promise-based library (eg. pouchdb).

The function then accepts 2 parameters, the first is the function that the data is passed into if everything goes well, the second is triggered when some error has occured.

As for catch, it’s just a shortcut to write then(null, function). So when an error is thrown, the error object is passed into the function.

The promise stack

You can use promises as callback functions, but there is a much nicer way to format your code.

remotedb.allDocs(...).then(function (resultOfAllDocs) {
    return localdb.put(...);
}).then(function (resultOfPut) {
    return localdb.get(...);
}).then(function (resultOfGet) {
    return localdb.put(...);
}).catch(function (err) {
    console.log(err);
});

There is one special feature about promises. If you return a promise in the function, the result is passed into the next then function. This way, you can chain different sections of code together. (The allDocs, then the put, then the get).

The catch function at the end is a good practice. Putting it at the end ensures that whenever an error is thrown, it will be caught. Regardless of which level the error occurs, the error will still be logged.

Advanced functions (resolve & reject)

When you create your own library using promises, you tend to face the need to wrap your synchronous code as asynchronous code. Here is where resolve comes in.

function someAPI(){
    return Promise.resolve().then(function(){
        // some error might be thrown here
        return "someSynchronousValue";    
    }).then(/* ... */);
}

This way, all your code will be promise-y, even the simple functions. This ensures code design consistency.

Besides, when you use promises this way, you can catch error easily. Gone are the days of slow and inefficient debugging.

So what about reject? The reject function returns a promise that is rejected immediately.

new Promise().resolve("some value").then(function(val){
    // do something to val
});

new Promise().reject(new Error("some error")).then(function(val){
   // do something to val
   // but this function will be skipped
}).catch(function(e){
    // this function is called immediately    
});

Promise.all (The foreach in promises)

There is another use case for promises. Let’s say we have a database query that returns a few rows, and we wanna update those rows. Typically, this is what the code will look like (in the minds of those non-promise ninjas).

getAllRows().then(function(result){
    result.rows.forEach(function(row){
        // update value of row
        row.val += 1;
        updateRow(row);              
    });
}).then(function () {
    // Naively believe all docs have been updated now!
});

But here is the right way to do it, with Promise.all of course.

getAllRows().then(function(result){
    return Promise.all(result.rows.map(function(row){
        //update value of row
        row.val += 1;
        return updateRow(row);
    }));
}).then(function(arrayOfUpdateRowResults){
    // all docs have been updated    
});

End

Well, all these are just the basics of using promises, there are certainly alot more to learn. If you are interested, I implore you to give this article a read. You will learn much much more.