Asynchronous Programming and Promises

Javascript is a synchronous language, meaning that code is executed one line at a time within a program. There are a few aspects of JS that are asynchronous, such as timers and AJAX calls, but for the most part the language is single-treaded. This constraint feels perfectly fine within some applications, but eventually you’ll need to be able to write JS code around an asynchronous event.

Without an idea of when an asynchronous operation will be executed, writing JS code that depends on the output of that operation can be tricky. One way to deal with this is through callbacks that are passed into the asynchronous function and executed upon its completion.

fs.readFile(‘/someFile’, function(err, data) {  
  if (err) {
    doSomething(err);
  } else {
  doSomethingElse(data);
  }
})

This method is perfectly valid, but generating a large pyramid of nested callbacks being passed down many asynchronous calls makes code a nightmare to read and debug.

This brings us to promises. Instead of passing a callback, we can create a promise that represents the result of an asynchronous operation. Once created, a promise can exist in one of three states: (1) pending, (2) resolved, or (3) rejected. The conditions by which a promise is resolved or rejected are described explicitly within the creation of the promise instance, and once triggered, the resolve and reject functions can pass data or errors down to then and catch blocks. Then blocks can be chained together and return either values or promises to be used as parameters by the next then block.

new Promise(function(resolve, reject) {  
  fs.readFile(‘/someFile’, function(err, data) {
    if (err) {
      reject(err);
    } else {
      resolve(data);
    }
  });
})
.then(function(data){   …   });
.catch(function(err){   …   });

Importantly, if a then block returns a promise, the next then block will not be executed until that promise has been fulfilled. Thus, we are able to execute as many nested asynchronous functions as we want without relying on any nested callbacks.

If you need to use the output of many asynchronous operations at once, building up an array of promises and passing it into Promise.all will allow you to wait on for the resolution of all of those promises. After Promise.all, a then block will take the array of resolved values as a parameter and a spread block will spread the array into individual arguments.

Unlike Promise.all, promise.race will take and array of promises and trigger the next block after the very first one is resolved.

Writing your own promises is no big deal (I refused to believe this for a while, but once you get used to it, promises are so very much easier than callbacks), but if you are executing an asynchronous operation that takes a callback and the callback is invoked with (err, results), you can simply pass the function into Promise.promisify to get a “promisified” version. Since this callback pattern is extremely common in nodejs modules, Promise.promisify can be a powerful tool and can help you get started using promises right away. In fact, if an entire library meets these callback requirements (fs, for example), you can pass the whole thing into Promise.promisifyAll to get a promisified version of every single function. PromisifyAll returns a library with “Async” suffixed versions of their original template functions (fs.writeFileAsync, fs.readFileAsync).

Promise.promisifyAll(fs);

fs.readFileAsync(‘/someFile’)  
  .then(function(data) {   …   })
  .catch(function(err){   …   })

var promises = [  
  fs.readFileAsync(‘/firstFile’),
  fs.readFileAsync(‘/secondFile’)    
]

Promises.all(promises)  
  .spread(function(firstFileData, secondFileData) {
    // do something with all the data!
  })