ES proposal: Promise.try()

[2017-08-09] dev, javascript, esnext, es proposal, async, promises
(Ad, please don’t block)

Update 2017-08-12: I added an FAQ.


The proposal “Promise.try()” by Jordan Harband is currently at stage 1. This blog post explains how it works.

Promise.try()  

To understand what Promise.try() does, consider the following code:

function countPlusOneAsync() {
    return countAsync()
    .then(result => {
        return result + 1;
    });
}

If countAsync() throws a synchronous exception then so does countPlusOneAsync(). But we want the latter function to deliver all errors via Promises. To avoid this problem, we can use Promise.try():

function countPlusOneAsync() {
    return Promise.try(() => { // (A)
        return countAsync(); // (B)
    })    
    .then(result => {
        return result + 1;
    });
}

Now errors thrown in line B will lead to the rejection of the Promise returned in line A. Promise.try() has two additional benefits:

  • The code is structured more consistently: all functionality is now located inside callbacks.
  • You control what Promise implementation is used. Whatever kind of thenable is returned by countAsync(), it will be converted to an instance of Promise.

Work-arounds  

Until we get Promise.try(), we can use the following work-around:

function countPlusOneAsync() {
    return Promise.resolve().then(() => { // (A)
        return countAsync();
    })    
    .then(result => {
        return result + 1;
    });
}

Promise.resolve() creates a Promise that is fulfilled with undefined. That result does not matter to us. What does matter is that we have just started a Promise chain and can put the code to try into the callback starting in line A.

Another work-around is to use the Promise constructor:

function countPlusOneAsync() {
    return new Promise(resolve => {
        resolve(countAsync());
    })
    .then(result => {
        return result + 1;
    });
}

FAQ  

Why not just use async functions?  

If you can use async functions, they are the better choice and automatically convert all exceptions thrown inside their bodies to rejections. Alas, sometimes you need to work with Promises directly and then Promise.try() is useful.

Why not just use new Promise(···)?  

Promise.try() is relatively small syntactic sugar and easy to polyfill. Compared to using the Promise constructor, it has the following advantages:

  • The code inside its callbacks is consistent with .then() callbacks and can be moved more easily.
  • It is more self-descriptive and slightly more concise.

Further reading