假设我们有一个包含汽车的 mongoDB,用户可以添加汽车。目的是防止多于一辆具有相同车牌的汽车被保存。堆栈是node/express 和mongoose。
场景:数据库中已存在具有所提供车牌的汽车。
第一次尝试:
console.log('Before check')
await Car.findOne({ licensePlate }, (err, foundCar) => {
if (err) return next(err)
if (foundCar) return res.status(400).send('license plate exists already in db.')
})
console.log('After check')
// some other code, saving to db and stuff
return res.status(200).send('The car was saved succesfully.')
我天真的期望是,如果我在 Moongooses findOne 函数前面加上一个await关键字,那么“await block ”后面的代码将不会被执行,因为找到了一辆车,然后通过发送400响应来终止请求。相反,执行“await block ”之后的代码会导致
Error: Can't set headers after they are sent.
第二次尝试:
console.log('Before check')
try {
const foundCar = await Car.findOne({ licensePlate })
if (foundCar) return res.status(400).send('license plate exists already in db.')
} catch (err) {
return next(err)
}
console.log('After check')
// some other code, saving to db and stuff
return res.status(200).send('The car was saved succesfully.')
这按预期工作,并防止任何代码在 if(foundCar) 检查后执行
问题:有人可以告诉我尝试一中发生了什么吗?现在来看,将回调与await关键字结合起来确实感觉很奇怪,但显然我对async/await的理解还不足以真正了解这里发生的情况。在这种情况下,await 是否意味着“代码等待”仅findOne 完成,然后回调和其余代码同时运行?任何指向有用资源以充分了解正在发生的事情的指针都将非常感激。
最佳答案
- 如果
findOne
函数未传递回调函数,它将返回一个 Promise 对象。 - 如果使用 Promise 对象调用
await
,它将等待直到 Promise 解析该值并返回该值。 - 如果使用非 Promise 对象调用
await
,它会将其转换为已解决的 Promise。
在第一种情况下,回调被传递给 findOne
,因此它不会返回 Promise 对象,并且 await
将其转换为已解析的 Promise。它的值在 Node.js 执行周期的 nextTick
中解析。因此,这里不需要等待结果发生,甚至在调用回调函数之前发送响应 200。当回调实际执行时,它会尝试再次发送响应 header ,并且由于它们已经发送,因此您会收到此错误。
在第二种情况下,findOne
实际上返回一个 Promise 对象。因此 await
会等待 Promise 解析为一个值。这就是为什么第二种情况可以正常工作的原因。
关于node.js - 将回调传递给以await 为前缀的函数。执行顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48418344/