Reputation: 1375
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);
Upvotes: 23
Views: 34043
Reputation: 111336
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:
Upvotes: 22
Reputation: 664484
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);
Upvotes: 1
Reputation: 203304
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(...)
Upvotes: 13