8 Tips for Mastering JavaScript Promises

In the previous post, Understanding JavaScript Promises, we learned how simple and useful Promises can be. Here are 8 tips to help you take advantage of that simplicity, and become a Master of Promises!

1. Know the terminology

Talking about Promises is much easier when you know the right terminology.

A Promise is pending while it is still working.

When the work is complete, the Promise is fulfilled (or resolved). The result of the work is called the fulfillment value.

When an error occurs, the Promise is rejected. The Error is called the rejection reason.

2. You don’t need Deferred!

The Deferred pattern (which includes both deferred objects and the new Promise constructor) was designed for wrapping low-level APIs, such as XmlHttpRequest and setTimeout. You should rarely need to do this work yourself!

When you chain, you no longer need to explicitly handle errors – all the “wiring” is automatic.

4. Watch for “runaway Promises”

Whenever you have a Promise, you have to do something with that Promise. If you’re not returning the Promise, then you should be handling the errors. Otherwise, a “runaway Promise” will not be connected to your Promise chain, and will easily cause race conditions or hide errors.

As a rule of thumb, the entry-point (eg. the UI layer, the request handler) should be handling errors, and the rest of your code should be returning the Promises.

6. Learn your library

In theory, a Promise only requires a .then method. If it is “thenable”, it’ll interop with any other Promise library. However, every Promise library adds its own host of additional features for handling everyday tasks. Learn your library, and you’ll unlock the power of your Promise’s potential!

7. NodeJS developers can promisify all the things!

The majority of NodeJS modules use an async pattern called error-first callbacks instead of Promises. Fortunately, there is an effortless way to convert any error-first-callback module into a Promise-returning module! An incredible utility, called promisifyAll, does this for you, and it couldn’t be easier to use:

This will give you the original fs module, unharmed. It still has its original methods, like fs.readFile(filename, callback) and fs.writeFile(filename, data, callback). However, the fs module has now been augmented with Promise-returning methods, called readFileAsync(filename) => Promise and writeFileAsync(filename, data) => Promise and so on!

// Now, I can use `readFileAsync` instead of `readFile`
fs.readFileAsync("data.txt").then(function(data) {
console.log(data);
});

The async + await syntax makes Promises an implementation detail, that you no longer need to think about! Technically, the getUserInfo function returns a Promise, as do getUserId, getProfile, and getProjects. However, the await keyword waits for the promise, and then gives you the value. It’s the same as calling .then to get the results, but this completely eliminates the callbacks and the nesting! This code looks almost identical to synchronous code.

If you’re not lucky enough to be bleeding-edge, or transpiling with something like Babel, you’ll have to stick with the Promise equivalent:

So when you look at Promise code like this, you should realize that 3 of these return statements are actually just await statements in disguise. Realize that this function only has one real return point: the final return { userId, profile, projects };