简体   繁体   中英

How to throw exception from async function

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);
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) { ... } 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:

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(...)

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM