Nodejs 重复拒绝(未处理的并且在 try/catch 块中)

Nodejs duplicate rejection (unhandled one and in a try/catch block)

通过使用 node js 12.16.1 LTS 我不明白为什么这段代码会导致 双重拒绝(一个未处理,一个被捕获)。 当我 删除 p promise 并等待 create_bug() 中的 p 时,它运行良好(只有一个拒绝在 try catch 块中捕获)。我不知道为什么。

Nodejs 专家,你能帮忙吗?

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

来自您的 superCreate 的变量 res 中包含的承诺 未等待 并且 未附加 在它被拒绝之前给它一个 catch 处理程序。因此未处理的承诺拒绝被触发。当在 main.

中触发等待时,拒绝后会附加一个处理程序

请注意,拒绝处理程序即使在被拒绝后附加到承诺上也会被调用。尝试例如:

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

请注意,您现在还会收到 "main1" 和 "main2" 错误。

或者,尝试从 superCreate 函数中删除 async,现在您应该会看到 In create, res = ${res} 没有打印出来,而是同步处理异常。

另一种选择是直接从 create_bug return res 而不等待 main 中的 res。然后您将看到与原始行为类似的行为:未处理的拒绝和 "normal" 捕获处理块。

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