使用 forEach 和 async/await,对于 Node 和 Jest 的行为不同

dev*_*ell 2 javascript node.js async-await jestjs

我有一个将数据写入 mongodb 的函数,如下所示:

const writeToDB = async (db, data) => {
  const dataKeys = Object.keys(data)
  dataKeys.forEach(async key => db.collection(key).insertMany(data[key]))
}
Run Code Online (Sandbox Code Playgroud)

如果我在节点脚本中运行它,效果很好。但是当我尝试在 Jest 中使用它时,beforeAll我从 Jest 中得到了这个异步错误:

测试运行完成后,Jest 一秒钟都没有退出。这通常意味着测试中存在未停止的异步操作。

经过一些故障排除后,我发现这forEach是造成问题的原因。使用for循环解决了这个问题:

const writeToDB = async (db, data) => {
  const dataKeys = Object.keys(data)
  for (const key of dataKeys) {
    await db.collection(key).insertMany(data[key])
  }
}
Run Code Online (Sandbox Code Playgroud)

搜索这个问题我遇到了这篇文章: https://codeburst.io/javascript-async-await-with-foreach-b6ba62bbf404

那里的解释很有道理,但它给我留下了一些疑问:

  • 根据该文章,即使在节点脚本中它也不应该起作用。怎么就这样了?
  • 为什么在 Node 中执行它与在 Jest 中运行它不同?

编辑

读完所有评论后,我意识到我的第一个问题有点无稽之谈。通常我将异步函数的结果分配给变量,如果我不放置等待,则会出现未定义的错误。但这里的情况并非如此,因此脚本正常退出,并且数据库写入在后台同时发生。

Est*_*ask 8

现有的答案已经详细解释了为什么forEach不应该与 Promise 一起使用它的使用方式。forEach回调不考虑返回的承诺并破坏承诺链。async..await需要与 with 一起使用for..of来串行评估 Promise,或者与Promise.alland 一起map使用来并行评估。

Jest 支持 Promise,并期望从异步函数( 等)返回的 Promiseit表示该函数中发生的异步过程已经结束。

一旦 Jest 完成测试,它就会检查是否存在阻止 Node 退出的打开句柄。由于 Jest 没有返回并链接 Promise,因此它们所代表的流程会阻止 Jest 完成测试流程。

该问题由上述错误消息表示:

测试运行完成后,Jest 一秒钟都没有退出。

这通常意味着测试中存在未停止的异步操作。请考虑使用 --detectOpenHandles 运行 Jest 来解决此问题。