Promise-based functions should not throw exceptions

By Axel Rauschmayer

Promise-based functions should only ever reject the Promises they return, they should never throw exceptions.

The reason is that doing so would disrupt Promise-based error handling:

    function asyncFunc() {
        return doSomethingAsync() // (A)
        .then(result => {
            ···
        })
        .catch(error => { // (B)
            ···
        });
    }

If the asynchronous function call in line A throws an exception then the error handler in line B will never be triggered.

Handling exceptions in Promise-based functions

If exceptions are thrown inside the callbacks of then() and catch() then that’s not a problem, because these two methods convert them to rejections.

However, things are different if you start your async function by doing something synchronous:

    function asyncFunc() {
        doSomethingSync(); // (A)
        return doSomethingAsync()
        .then(result => {
            ···
        });
    }

If an exception is thrown in line A then the whole function throws an exception. There are two solutions to this problem.

Solution 1: returning a rejected Promise

You can catch exceptions and return them as rejected Promises:

    function asyncFunc() {
        try {
            doSomethingSync();
            return doSomethingAsync()
            .then(result => {
                ···
            });
        } catch (err) {
            return Promise.reject(err);
        }
    }

Solution 2: executing the sync code inside a callback

You can also start a chain of then() method calls via Promise.resolve() and execute the synchronous code inside a callback:

    function asyncFunc() {
        return Promise.resolve()
        .then(() => {
            doSomethingSync();
            return doSomethingAsync();
        })
        .then(result => {
            ···
        });
    }

TC39 agrees with not mixing exceptions and rejections

Brian Terlson points out that TC39 also thinks that Promise-based functions should never throw exceptions: Originally, if an async function had a default value that threw an exception then the function would throw an exception. Now, the function rejects the Promise it returns.

Further reading

Acknowledgements: this post was inspired by a post by user Mörre Noseshine in the “Exploring ES6” Google Group. Im also thankful for the feedback to a tweet asking whether it is OK to throw exceptions from Promise-based functions.

Source:: 2ality