使用forEach和async / await,节点和Jest的行为不同
我有一个函数将数据写入mongodb,如下所示:
const writeToDB = async (db, data) => {
const dataKeys = Object.keys(data)
dataKeys.forEach(async key => db.collection(key).insertMany(data[key]))
}
如果我在节点脚本中运行它,这可以正常工作。但是当我试图在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])
}
}
搜索这个问题我遇到了这篇文章:
那里的解释是有道理的,但它给我留下了一些问题:
- 根据这篇文章,它甚至不应该在节点脚本中工作。怎么回事呢?
- 为什么在节点中执行它与在Jest中运行它不同?
编辑
在阅读完所有评论之后,我意识到我的第一个问题有点无稽之谈。通常我将异步函数的结果分配给变量,如果我没有等待,那么行下面会有一个未定义的错误。但这不是这种情况,因此脚本正常退出,db写入在后台同时发生。
回答如下:现有的答案已经详细解释了为什么forEach
不应该像使用它一样使用承诺。 forEach
回调不会将退回的承诺考虑在内并打破承诺链。 async..await
需要与for..of
一起用于评估系列中的承诺,或与Promise.all
和map
进行并行评估。
Jest支持promises并期望从异步函数(it
等)返回的promise表示此函数中发生的异步进程已结束。
一旦Jest完成测试,它会检查是否存在阻止Node退出的打开句柄。由于承诺没有被Jest返回和链接,它们所代表的流程阻止了Jest完成测试过程。
此错误消息表示此问题:
测试运行完成后,Jest没有退出一秒钟。
这通常意味着您的测试中没有停止异步操作。考虑使用--detectOpenHandles运行Jest来解决此问题。