Node.JS:unhandledRejection与未等待的异步函数调用

nkhmeac6  于 2022-12-18  发布在  Node.js
关注(0)|答案(2)|浏览(186)

编辑:总的来说,这个问题是关于node.js中的unhandledRejection的。我没有期待一个没有被期待的承诺(但后来失败了)来使程序崩溃-因为我没有意识到unhandledRejection被抛出并导致node.js退出。刷新我对unhandledRejection的理解,我现在意识到**程序中任何地方的任何承诺都会被拒绝而没有catch()语句将抛出unhandledRejection并在默认情况下退出node.js进程。**这对我来说没有太大意义,但至少我理解了为什么会发生这种情况。

在下面的代码示例中,thisFailsLater最初成功返回,但稍后会引发错误。
这会导致runTest()传递try/catch并继续下一个等待。
但是,在等待下一个await调用时,thisFailsLater抛出一个错误,导致Node.JS退出:

% node t.js 
Throwing error in 2 seconds
thisFailsLater returned successfully
t.js:4
  throw new Error('thrown error in thisFails()')
        ^

Error: thrown error in thisFails()
    at thisFails (t.js:4:9)

这是我没想到的:看起来一个异步函数最初可以成功返回,但是任何被调用但没有在返回后抛出的函数内等待的异步函数将使Node.JS进程崩溃。
我对这个工作原理的理解正确吗?如果正确,当thisFailsLater()在成功返回后抛出2秒时,我如何阻止Node.JS退出?这完全打破了我对Promises工作原理的理解(只能解决或出错一次)。
错误再现:

async function thisFails() {
  console.log('Throwing error in 2 seconds')
  await new Promise((resolve) => setTimeout(resolve, 2e3));
  throw new Error('thrown error in thisFails()')
}

async function thisFailsLater() {
  try {
    // NOTE: intentionally NOT awaiting this call here.
    thisFails()
  } catch (err) {
    console.log('thisFailsLater caught error', err)
  }
}

async function runTest() {
  try {
    await thisFailsLater()
  } catch (err) {
    console.error('runTest caught error', err)
  }
  console.log('thisFailsLater returned successfully')
  await new Promise((resolve) => setTimeout(resolve, 3e3));
}

runTest().then(() => {
  process.exit(0)
}).catch((err) => {
  // NOTE: the error that Node.JS crashes with is NOT logged here!
  console.error('Caught error in main()', err)
  process.exit(1)
})
taor4pac

taor4pac1#

看起来异步函数最初可以成功返回,但是在返回后抛出的函数中调用但未等待的任何异步函数都将导致Node.JS进程崩溃。
我说的对吗?
是的,但这并不是async函数所独有的--在node.js中任何函数都可以做到这一点:

function failLater() {
    setTimeout(() => {
        throw new Error("crash!");
    }, 1000);
    return "success"
}

如果是这样,当thisFailsLater()在成功返回2秒后抛出时,如何防止Node.JS退出?
别那么做。
不要让函数异步引发异常。
要防止nodejs退出,可以使用hook on the global error and unhandledrejection events,但这些都是用于日志记录的-您的应用程序已经遇到了不可恢复的错误。
这完全打破了我对承诺如何工作的理解(只能解决或出错一次)。
它与promise无关,promise * do * 只在async函数体结束求值时结算一次,thisFails()是第二个单独的promise -独立于执行流,因为你没有await它。

x8diyxa7

x8diyxa72#

问题发生在以下位置:

async function thisFailsLater() {
  try {
    // NOTE: intentionally NOT awaiting this call here.
    thisFails()
  } catch (err) {
    console.log('thisFailsLater caught error', err)
  }
}

当你不对承诺执行await操作时,你就无法处理拒绝,await关键字所做的事情之一就是将一个被拒绝的承诺“转换”为异步上下文中的异常--来自await的MDN文档:
如果承诺被拒绝,则await表达式将引发被拒绝的值。包含await表达式的函数将出现在错误的堆栈跟踪中。否则,如果未等待被拒绝的承诺或立即返回被拒绝的承诺,则调用方函数将不会出现在堆栈跟踪中。
因为你没有await被拒绝的承诺,它不能在你的try块中被捕获,转换为异常,并在控制流转换期间作为参数传递给catch子句。如果你没有await承诺,但想处理拒绝,你必须使用.catch()方法承诺语法:

function thisFailsLater() {
  return thisFails().catch(exception => console.log('thisFailsLater caught error', exception));
}

相关问题