Promise
Promise
The Promise class is something that exists in many modern JavaScript engines and can be easily polyfilled. The main motivation for promises is to bring synchronous style error handling to Async / Callback style code.
Callback style code
In order to fully appreciate promises let's present a simple sample that proves the difficulty of creating reliable Async code with just callbacks. Consider the simple case of authoring an async version of loading JSON from a file. A synchronous version of this can be quite simple:
import fs = require('fs');
function loadJSONSync(filename: string) {
return JSON.parse(fs.readFileSync(filename));
}
// good json file
console.log(loadJSONSync('good.json'));
// non-existent file, so fs.readFileSync fails
try {
console.log(loadJSONSync('absent.json'));
}
catch (err) {
console.log('absent.json error', err.message);
}
// invalid json file i.e. the file exists but contains invalid JSON so JSON.parse fails
try {
console.log(loadJSONSync('invalid.json'));
}
catch (err) {
console.log('invalid.json error', err.message);
}There are three behaviors of this simple loadJSONSync function, a valid return value, a file system error or a JSON.parse error. We handle the errors with a simple try/catch as you are used to when doing synchronous programming in other languages. Now let's make a good async version of such a function. A decent initial attempt with trivial error checking logic would be as follows:
Simple enough, it takes a callback, passes any file system errors to the callback. If no file system errors, it returns the JSON.parse result. A few points to keep in mind when working with async functions based on callbacks are:
Never call the callback twice.
Never throw an error.
However, this simple function fails to accommodate for point two. In fact, JSON.parse throws an error if it is passed bad JSON and the callback never gets called and the application crashes. This is demonstrated in the below example:
A naive attempt at fixing this would be to wrap the JSON.parse in a try catch as shown in the below example:
However, there is a subtle bug in this code. If the callback (cb), and not JSON.parse, throws an error, since we wrapped it in a try/catch, the catch executes and we call the callback again i.e. the callback gets called twice! This is demonstrated in the example below:
This is because our loadJSON function wrongfully wrapped the callback in a try block. There is a simple lesson to remember here.
Simple lesson: Contain all your sync code in a try catch, except when you call the callback.
Following this simple lesson, we have a fully functional async version of loadJSON as shown below:
Admittedly this is not hard to follow once you've done it a few times but nonetheless it’s a lot of boiler plate code to write simply for good error handling. Now let's look at a better way to tackle asynchronous JavaScript using promises.
Creating a Promise
A promise can be either pending or fulfilled or rejected.

Let's look at creating a promise. It's a simple matter of calling new on Promise (the promise constructor). The promise constructor is passed resolve and reject functions for settling the promise state:
Subscribing to the fate of the promise
The promise fate can be subscribed to using .then (if resolved) or .catch (if rejected).
TIP: Promise Shortcuts
Quickly creating an already resolved promise:
Promise.resolve(result)Quickly creating an already rejected promise:
Promise.reject(error)
Chain-ability of Promises
The chain-ability of promises is the heart of the benefit that promises provide. Once you have a promise, from that point on, you use the then function to create a chain of promises.
If you return a promise from any function in the chain,
.thenis only called once the value is resolved:
You can aggregate the error handling of any preceding portion of the chain with a single
catch:
The
catchactually returns a new promise (effectively creating a new promise chain):
Any synchronous errors thrown in a
then(orcatch) result in the returned promise to fail:
Only the relevant (nearest tailing)
catchis called for a given error (as the catch starts a new promise chain).
A
catchis only called in case of an error in the preceding chain:
The fact that:
errors jump to the tailing
catch(and skip any middlethencalls) andsynchronous errors also get caught by any tailing
catch.
effectively provides us with an async programming paradigm that allows better error handling than raw callbacks. More on this below.
TypeScript and promises
The great thing about TypeScript is that it understands the flow of values through a promise chain:
Of course it also understands unwrapping any function calls that might return a promise:
Converting a callback style function to return a promise
Just wrap the function call in a promise and
rejectif an error occurs,resolveif it is all good.
E.g. let's wrap fs.readFile:
The most reliable way to do this is to hand write it and it doesn't have to be as verbose as the previous example e.g. converting setTimeout into a promisified delay function is super easy:
Note that there is a handy dandy function in NodeJS that does this node style function => promise returning function magic for you:
Webpack supports the
utilmodule out of the box and you can use it in the browser as well.
If you have a node callback style function as a member be sure to bind it as well to make sure it has the correct this:
Revisiting the JSON example
Now let's revisit our loadJSON example and rewrite an async version that uses promises. All that we need to do is read the file contents as a promise, then parse them as JSON and we are done. This is illustrated in the below example:
Usage (notice how similar it is to the original sync version introduced at the start of this section 🌹):
The reason why this function was simpler is because the "loadFile(async) + JSON.parse (sync) => catch" consolidation was done by the promise chain. Also the callback was not called by us but called by the promise chain so we didn't have the chance of making the mistake of wrapping it in a try/catch.
Parallel control flow
We have seen how trivial doing a serial sequence of async tasks is with promises. It is simply a matter of chaining then calls.
However, you might potentially want to run a series of async tasks and then do something with the results of all of these tasks. Promise provides a static Promise.all function that you can use to wait for n number of promises to complete. You provide it with an array of n promises and it gives you an array of n resolved values. Below we show Chaining as well as Parallel:
Sometimes, you want to run a series of async tasks, but you get all you need as long as any one of these tasks is settled. Promise provides a static Promise.race function for this scenario:
Last updated