23

I have an async function that I expect to throw exception on failure. However something seems to preventing this:

by omitting the try catch blocks I expect an exception to be thrown which I want to handle outside of the function.

The actual result I get is somewhat confusing:

(node:10636) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): E11000 duplicate key error index.

(node:10636) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

async f(obj) {
    await db.collection('...').save(obj);
}

I get the same result when I try to catch the exception and throw something else instead:

async f(obj) {
    try {
        await db.collection('...').save(obj);
    } catch(e) {
        throw e.message;
    }
}

The function is called from a try block, so don't see how this is an Unhandled Promise.

I am trying to use f as a parameter of another function:

g(obj, handler) {
    try {
        handler(obj);
    } catch(e);
        ...
    }
}

g(objToSave, f);
1
  • As a side note, your rethrow is incorrect... You should be using catch(e) { throw e; }. Because (1) e.message is a string and you should not just throw a string and (2) e may include other parameters that would be useful at the next level. Commented Dec 21, 2018 at 16:46

3 Answers 3

22
async f(obj) {
    try {
        await db.collection('...').save(obj);
    } catch(e) {
        throw e.message;
    }
}

The function is called from a try block, so don't see how this is an Unhandled Promise.

What is unhandled here is the rejection of a promise returned by the f() function, not by the .save() method. So this will not cause that problem:

async f(obj) {
    try {
        await db.collection('...').save(obj);
    } catch(e) {
        console.error(e.message);
    }
}

Throwing an exception in async function always rejects the promise that is returned by that function.

To catch the exception you either have to do this in another async function:

try {
    asyncFunc();
} catch (err) {
    // you have the error here
}

or you can add a rejection handler explicitly:

asyncFunc().catch(err => {
    // you have the error here
});

If you are catching the exception and throwing another exception then you get the same problem, just in a different function.

You either have to add a promise rejection handler and not throw an exception or return a rejected promise there - or run that function in another async function that handles the exception instead of rethrowing the same or new exception.

To sum it up: Every async function returns a promise. Every promise needs to have a rejection handler.

The rejection handler is added using a two-function .then() or with .catch(), or with try { await asyncFunction(); } catch (err) { ... }

When you have a promise rejection with no rejection handler, you will get a warning in older versions of Node and a fatal error in newer versions of Node - see this answer for more details:

Sign up to request clarification or add additional context in comments.

4 Comments

I inferred from "The function is called from a try block" that the OP means the f function is being called from a try/catch block.
The call of the f function looks like: try{ f() } catch(e) { console.log(e) } inside a non-async function. If I rewrite to f().catch(...) it works, however I would prefer another option because f is a parameter of the calling function and might not return a promise.
@user1063963 if you're calling it from a non-async function, it's not going to work: asynchonicity is "viral", each calling function has to either be async as well (and you'd use await), or you need to properly handle the returned promise (if there is one). You should probably update your question to reflect what exactly it is you're doing.
I think you forgot an await in try { asyncFunc(); }
13

Ultimately, what you're trying to do is calling an asynchronous function f in a synchronous function g, which won't work (that would be the equivalent of being able to turn an asynchronous function into a synchronous function).

Instead, g either has to be async as well, or it should properly handle the promise returned by f. However, in this comment you state that the function represented by f may not always return a promise, in which case the former option would be the easiest to implement:

async g(obj, handler) {
  return await handler(obj);
}

This will also work if handler doesn't return a promise, but just a value (which is documented here).

Calling g (again) requires either an async function, or code to handle its returned promise:

g(objToSave, f).then(...).catch(...)

2 Comments

The simpler form is return Promise.resolve(handler(obj)). return await is rarely if ever necessary.
@lonesomeday true, but return await looks better IMO :)
1

which I want to handle outside of the function

That's the only thing you forgot to do. (Which is why the warning is complaining about an unhandled rejection). Your function f is working fine.

You cannot throw a synchronous exception from an async function, everything is asynchronous and exceptions will lead to a rejection of the result promise. That is what you will need to catch:

function g(obj, handler) {
    try {
        Promise.resolve(handler(obj)).catch(e => {
            console.error("asynchronous rejection", e);
        });
    } catch(e) {
        console.error("synchronous exception", e);
    }
}
// or just
async function g(obj, handler) {
    try {
        await handler(obj);
//      ^^^^^
    } catch(e) {
        console.error("error", e);
    }
}

g(objToSave, f);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.