By using node js 12.16.1 LTS I don't understand why this piece of code leads to a double rejection (one unhandled and one catched). When I remove the p promise and await p in create_bug(), it works well (Only one rejection catched in a try catch block). I cannot figure out why.
Nodejs experts, could you please help ?
'use strict';
process.on('uncaughtException', (err) => {
console.error(`uncaughtException: ${JSON.stringify({name: err.name, msg: err.message})}`);
});
process.on('unhandledRejection', (err) => {
console.error(`unhandledRejection: ${JSON.stringify({name: err.name, msg: err.message})}`);
});
async function create_bug() {
console.log('In create');
let res = superCreate();
console.log(`In create, res = ${res}`);
let p = new Promise((a, r) => setTimeout(() => a(), 0));
await p;
return res;
}
async function superCreate() {
console.log('superCreate : now throwing');
throw new Error("Something wrong");
}
async function create_OK() {
console.log('In create');
let res = await superCreate();
console.log(`In create, res = ${res}`);
let p = new Promise((a, r) => setTimeout(() => a(), 0));
await p;
return res;
}
async function main() {
try {
let res = await create_bug();
console.log(`create result : ${res}`);
} catch (err) {
console.error(`ERROR caught in main : ${JSON.stringify({name: err.name, msg: err.message})}`);
}
}
main().then(() => {
setTimeout(() => console.log(`Finished`), 2000);
});
The promise contained in the variable res
from your superCreate
is not awaited and there is not attached a catch
handler to it before it gets rejected. Therefore the unhandled promise rejection is triggered. A handler is attached after the rejection when the await is triggered in main
.
Note that a rejection handler is invoked even though it is attached on a promise after it is rejected. Try e.g.:
async function main() {
let res = create_bug();
try {
await res;
console.log(`create result : ${res}`);
} catch (err) {
console.error(`ERROR caught in main : ${JSON.stringify({name: err.name, msg: err.message})}`);
}
res.catch(err => console.error(`main1: ${JSON.stringify({name: err.name, msg: err.message})}`));
res.catch(err => console.error(`main2: ${JSON.stringify({name: err.name, msg: err.message})}`));
}
Notice that you will now also get the "main1" and "main2" errors.
Alternatively, try removing the async
from the superCreate
function, now you should see that the In create, res = ${res}
is not printed, but instead the exception is handled synchronously.
Yet another alternative is to simply return res directly from create_bug
without any await and instead await the res
in main
. Then you will see similar behavior to your original: both an unhandled rejection and the "normal" catch-handling block.
async function create_bug() {
console.log('In create');
let res = superCreate();
console.log(`In create, res = ${res}`);
return res;
}
async function superCreate() {
console.log('superCreate : now throwing');
throw new Error("Something wrong");
}
async function main() {
try {
let res = create_bug();
let p = new Promise((a, r) => setTimeout(() => a(), 0));
await p;
await res;
console.log(`create result : ${res}`);
} catch (err) {
console.error(`ERROR caught in main : ${JSON.stringify({name: err.name, msg: err.message})}`);
}
}