This code get's unhandledRejection
error and I don't know why.
If the Error is thrown in try/catch, shouldn't it be caught by Catch Expression?
async function main () {
try {
await run(throwError)
} catch (error) {
console.log('main catch error', error);
}
}
async function run (callback) {
return new Promise(async resolve => {
await throwError()
});
}
async function throwError () {
throw new Error('custom error')
}
process.on('unhandledRejection', (reason, promise) => {
console.log('unhandledRejection - reason', reason, promise);
})
main()
CodePudding user response:
It's not caught because you're passing an async
function into new Promise
. An error inside an async
function rejects the promise that the function returns. The Promise
constructor doesn't do anything a promise returned by the function you pass it (the return value of that function is completely ignored), so the rejection goes unhandled. This is one of the promise anti-patterns: Don't provide a promise to something that won't handle it (like addEventListener
on the web, or the Promise
constructor, or forEach
, ...).
Similarly, there's no reason to use new Promise
in your async
function at all. async
functions already return promises. That's another anti-pattern, sometimes called the explicit promise construction anti-pattern. (But see below if you're wrapping an old-fashioned callback API.)
If you remove the unnecessary new Promise
, it works as you expect (I also updated run
to call callback
rather than ignoring it and calling throwError
directly):
async function main() {
try {
await run(throwError);
} catch (error) {
console.log("main catch error", error);
}
}
async function run(callback) {
return await callback();
}
async function throwError() {
throw new Error("custom error");
}
process.on("unhandledRejection", (reason, promise) => {
console.log("unhandledRejection - reason", reason, promise);
});
main();
About the return await callback();
in that example: Because whatever you return from an async
function is used to settle the function's promise (fulfilling it if you return a non-thenable, resolving it to the thenable if you return one), and that return
is at the top level of the function (not inside a try
/catch
or similar), you could just write return callback();
. In fact, you could even remove async
from run
and do that. But keeping the await
makes the async stack trace clearer on some JavaScript engines, more clearly indicates what you're doing, and keeps working correctly even if someone comes along later and puts a try
/catch
around that code.
In a comment, you said that you used new Promise
because you were wrapping around a callback-based API. Here's how you'd do that (see also the answers here):
// Assuming `callback` is a Node.js-style callback API, not an
// `async` function as in the question:
function run(callback) {
return new Promise((resolve, reject) => {
callback((err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
but, you don't have to do that yourself if the callback-based API uses the standard Node.js convention as above, there's a promisify
function in utils
.
CodePudding user response:
You don't want to throw an error in this case, you want to invoke reject
:
return new Promise((resolve, reject) => {
reject('custom error')
});
If the error being thrown is out of your control, you can catch it inside the promise implementation and reject in that case.