我正在尝试将以下数据播种到我的MongoDB服务器中:
const userRole = {
role: 'user',
permissions: ['readPost', 'commentPost', 'votePost']
}
const authorRole = {
role: 'author',
permissions: ['readPost', 'createPost', 'editPostSelf', 'commentPost',
'votePost']
}
const adminRole = {
role: 'admin',
permissions: ['readPost', 'createPost', 'editPost', 'commentPost',
'votePost', 'approvePost', 'approveAccount']
}
const data = [
{
model: 'roles',
documents: [
userRole, authorRole, adminRole
]
}
]
当我尝试遍历此对象/数组并将此数据插入数据库时,最终得到的是“ adminRole”的三个副本,而不是三个单独的角色。我无法弄清楚为什么会发生,这使我感到非常愚蠢。
以下是我实际遍历对象并将其作为种子的代码,并且我知道它实际上正在获取每个值,因为我已经完成了console.log测试并且可以正确获取所有数据:
for (i in data) {
m = data[i]
const Model = mongoose.model(m.model)
for (j in m.documents) {
var obj = m.documents[j]
Model.findOne({'role':obj.role}, (error, result) => {
if (error) console.error('An error occurred.')
else if (!result) {
Model.create(obj, (error) => {
if (error) console.error('Error seeding. ' + error)
console.log('Data has been seeded: ' + obj)
})
}
})
}
}
更新:
这是我阅读每个人的回复后想到的解决方案。两个私有函数生成Promise对象,用于检查数据是否存在以及插入数据,然后Promise.all满足所有Promise。
// Stores all promises to be resolved
var deletionPromises = []
var insertionPromises = []
// Fetch the model via its name string from mongoose
const Model = mongoose.model(data.model)
// For each object in the 'documents' field of the main object
data.documents.forEach((item) => {
deletionPromises.push(promiseDeletion(Model, item))
insertionPromises.push(promiseInsertion(Model, item))
})
console.log('Promises have been pushed.')
// We need to fulfil the deletion promises before the insertion promises.
Promise.all(deletionPromises).then(()=> {
return Promise.all(insertionPromises).catch(()=>{})
}).catch(()=>{})
我不会同时包括promiseDeletion
和promiseInsertion
,因为它们在功能上是相同的。
const promiseDeletion = function (model, item) {
console.log('Promise Deletion ' + item.role)
return new Promise((resolve, reject) => {
model.findOneAndDelete(item, (error) => {
if (error) reject()
else resolve()
})
})
}
更新2:您应该忽略我的最新更新。我已经稍微修改了发布的结果,但是即使那样,一半的时间角色也被删除而不插入。至于何时将角色实际插入服务器中,这是非常随机的。在这一点上,我感到非常困惑和沮丧。
答案 0 :(得分:0)
使用Javascript时,您会遇到一个非常常见的问题:您不应该在常规的for(-in)循环中定义(异步)函数。发生的是,当您遍历这三个值时,将调用第一个异步查找。由于您的代码是异步的,因此nodejs在继续进行下一个循环迭代并计数到第三个值(此处为管理规则)之前,不会等待其完成。 现在,由于您在循环中定义了函数,因此当第一个异步调用结束时,for循环已经循环到了最后一个值,这就是为什么admin被插入三次的原因。
为避免这种情况,您可以将异步函数移出循环,以按值而不是引用强制调用。尽管如此,这还会带来很多其他问题,所以我建议您宁愿看一下Promise以及如何链接它们(例如,将所有猫鼬Promise放入一个数组中,然后使用Promise.all等待它们)或使用更加现代的async / await语法以及for-of循环,可轻松阅读并提供顺序的异步命令指令。
检查这个非常相似的问题:Calling an asynchronous function within a for loop in JavaScript
注意:for-of正在讨论性能方面的问题,因此请检查这是否适用于您的用例。
答案 1 :(得分:0)
在循环中使用异步函数可能会导致一些问题。
您应该更改使用findOne的方式以使其具有同步功能
首先,您需要将函数设置为异步,然后像这样使用findOne:
async function myFucntion() {
let res = await Model.findOne({'role':obj.role}).exec();//Exec will fire the function and give back a promise which the await can handle.
//do what you need to do here with the result..
}