为啥 UnhandledPromiseRejectionWarning 只出现在生产中(Google Cloud Run)?

Posted

技术标签:

【中文标题】为啥 UnhandledPromiseRejectionWarning 只出现在生产中(Google Cloud Run)?【英文标题】:Why is UnhandledPromiseRejectionWarning only appearing in production (Google Cloud Run)?为什么 UnhandledPromiseRejectionWarning 只出现在生产中(Google Cloud Run)? 【发布时间】:2022-01-18 17:00:55 【问题描述】:

我很难解决一些在我的笔记本电脑开发环境中运行良好的功能(即使我使用 NODE_ENV=production 运行它),但是当我将它部署到 Google Cloud Run 时,我会遇到错误,例如

Default
2021-12-15T12:16:35.016406Z at TCP.done (_tls_wrap.js:564:7)
Default
2021-12-15T12:16:35.016414Z(Use `node --trace-warnings ...` to show where the warning was created)
Default
2021-12-15T12:16:35.016426Z(node:1) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
Default
2021-12-15T12:16:35.116284Z(node:1) [DEP0018] 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.
Default
2021-12-15T12:16:35.116931Z(node:1) UnhandledPromiseRejectionWarning: Error: socket hang up
Default
2021-12-15T12:16:35.116954Z at connResetException (internal/errors.js:639:14)
Default
2021-12-15T12:16:35.116969Z at TLSSocket.socketCloseListener (_http_client.js:449:25)
Default
2021-12-15T12:16:35.116979Z at TLSSocket.emit (events.js:412:35)
Default
2021-12-15T12:16:35.116989Z at net.js:686:12
Default
2021-12-15T12:16:35.116999Z at TCP.done (_tls_wrap.js:564:7)

导致我的脚本停止。在我的本地版本中,它会继续运行而不会停止。这非常令人沮丧,因为我需要一直将代码上传到 Cloud Run 以测试我的代码。我正在盲目测试它,因为错误没有出现在我的开发版本中。

我对 Node.js 还是很陌生,并且经常在使用 .then()、链接 .then()、promise、使用 resolve() 时遇到困难,因此代码很可能是错误的。

我的问题在这里:

    为什么这个错误只出现在 Cloud Run 上? 如何以相同的方式配置我的本地计算机,使其显示相同的警告和错误 - 以便解决此问题?

我的代码(简化):

function createOrUpdateSpotterData(data) 

   // NOTE: Data comes from API and not from DB, thus no info such as twitter_status
   // TODO: Separate into a create or update, in order to not check twitter ID every time.
   return new Promise( async function (resolve)  
      var twitterId = await twitterApi.getTwitterIdFromUsername(cleanedUsername)
      data['twitter_numeric'] = twitterId    
      resolve(data)

   ).then( (data) => 
      db.collection('spotters').doc(data.marker).set(data) 
   ).catch( (error) =>  console.log(error) )


async function updateSpottersJsonToDb() 
   console.log("> updateSpottersJsonToDb")
   return new Promise( (resolve, reject) => 
      readStorageData(remote_files['spotters'])
         .then( async (spotterListJson) => 
            var spotterList = JSON.parse(spotterListJson)       
            await spotterList.forEach((spotter) => 
               createOrUpdateSpotterData(spotter)
            )                         
         )
         .then(() =>  
            resolve() 
         )
         .catch((error) => console.error(error))
   ).catch((error) => console.error(error))

【问题讨论】:

对于初学者,您可以停止使用将现有 Promise 包装在另一个手动创建的 Promise 中的 Promise 反模式。当你这样做时,在错误处理中很容易出错,这是完全没有必要的。您可以只使用代码已经生成的第一个承诺 - 无需将其包装在新的承诺中。 createOrUpdateSpotterData() 是异步的吗?仅供参考,await spotterList.forEach(...) 中的await 什么都不做,因为.forEach() 没有返回值并且不知道asyncawait 仅在您等待承诺时才做某事。如果此循环中有异步内容,请将其更改为普通的 for 循环和 await 循环内的实际异步操作。 另外,你知道.catch((error) => console.error(error)) 会记录错误然后“吃掉”它,这样调用者就不会看到错误了吗? 感谢您的 cmets。我知道当我在编码时学习时,我可能会犯很多非常糟糕的错误。然而,我发现很难对 promise、.then 和 await 有一个很好的直觉。 1.你是什么意思,具体来说,承诺反模式。你的意思是在新的 Promise 中有 .then() 吗? 2. createOrUpdateSpotterData() 不是异步的。如果是这样,它会改变什么吗?感谢您提供关于 await 和 forEach 的提示,我有这种感觉,但无法确认。 【参考方案1】:

承认这一点很尴尬,但我对这个“只见树木不见森林”的“错误信息”的数量感到有点不知所措。毕竟这只是一个警告,通过过滤std.out 上的日志(在 Google Cloud Logs 上),我意识到这并没有导致我在应用程序中遇到的任何问题。

然而,这个问题的原因是我没有正确捕获我的 Promise 中的错误,这确实是另一个问题 (being discussed here)。

如果您稍后通过 Google 找到此内容,最好知道您可以使用 process.on() 隐藏此警告。

【讨论】:

以上是关于为啥 UnhandledPromiseRejectionWarning 只出现在生产中(Google Cloud Run)?的主要内容,如果未能解决你的问题,请参考以下文章

你应该同步运行方法吗?为啥或者为啥不?

为啥使用 glTranslatef?为啥不直接更改渲染坐标?

为啥 DataGridView 上的 DoubleBuffered 属性默认为 false,为啥它受到保护?

为啥需要softmax函数?为啥不简单归一化?

为啥 g++ 需要 libstdc++.a?为啥不是默认值?

为啥或为啥不在 C++ 中使用 memset? [关闭]