[英]Waiting on a long-running code block in a loop
So I'm trying to wrap my head around why my loop doesn't appear to be executing in the right order.所以我想弄清楚为什么我的循环似乎没有以正确的顺序执行。 This is the call made by the worker thread when it's done sorting out the values.
这是工作线程在整理完值后发出的调用。
worker.on('message',function(data) {
console.log(data)
for (var i of data.entries) {
console.log(i.Email)
db.rota.checkUserHasShifts(i.Email,function(flag) {
if (flag) {
console.log('e', i.Email)
db.rota.getShiftsForUser(i.Email,function(err, shiftData) {
if (!shiftData) return
shiftData.Shifts = i.Shifts
shiftData.save(function(err) {
if (err) throw err;
})
})
} else {
console.log('n', i.Email)
var newShift = new db.rota({Email:i.Email,Shifts:i.Shifts})
newShift.save(function (err){
if (err) throw err
});
}
})
};
console.log("Spreadsheet processed")
})
For a set of 20 entries, this will:对于一组 20 个条目,这将:
{Email:"someemail",Shifts:{date:time}}
objects in an array{Email:"someemail",Shifts:{date:time}}
对象n lastemail@example.com
in console and 20 identical entries in the database.n lastemail@example.com
和数据库中的 20 个相同条目。 The intended behaviour is obviously to wait for each entry before moving on to the next, or at least to queue an entry for each email in the set.预期的行为显然是在继续下一个条目之前等待每个条目,或者至少为集合中的每封电子邮件排队一个条目。 How do I get the loop to wait and properly execute the db.rota.checkUserHasShifts method and callback on all the entries, not just the last one?
如何让循环等待并正确执行 db.rota.checkUserHasShifts 方法并回调所有条目,而不仅仅是最后一个?
You have to scope your i variable before calling checkUserHasShifts
otherwise it will be called 20 times with the last i
value in your loop.您必须在调用
checkUserHasShifts
之前checkUserHasShifts
i 变量的范围,否则它将使用循环中的最后一个i
值调用 20 次。
for (var i in items){
(function(i){ db.rota.checkUserHasShifts(i)..})(i);
}
to understand why, I found section 6 "Scope Revisited" of this excellent article very useful (although it is targeted at folks coming from a C# background):为了理解原因,我发现这篇优秀文章的第 6 节“范围重访”非常有用(尽管它针对的是具有 C# 背景的人):
https://mauricebutler.wordpress.com/tag/c-javascript/ https://mauricebutler.wordpress.com/tag/c-javascript/
Relevant quote:相关报价:
JavaScript does not have "Block Scope" thus the for loop has not introduced new scope.
JavaScript 没有“块作用域”,因此 for 循环没有引入新的作用域。 This means that each time the element variable is accessed, the same memory location is updated
这意味着每次访问元素变量时,都会更新相同的内存位置
This SO question also demonstrates the effect very well: JavaScript closure inside loops – simple practical example这个SO问题也很好地展示了效果: 循环内的JavaScript闭包——简单实用的例子
The easiest way to approach this is with Promises and async/await - makes the code very clean compared to using callbacks that check if there's more to process解决这个问题的最简单方法是使用 Promises 和 async/await - 与使用检查是否有更多处理的回调相比,使代码非常干净
So, firstly, if you "promisify" the checkUserHasShifts
and getShiftsForUser
functions, the main loop is surprisingly simple!因此,首先,如果您“承诺”
checkUserHasShifts
和getShiftsForUser
函数,主循环非常简单!
Also, creating functions that handle .save
and return a Promise also makes it simpler此外,创建处理
.save
并返回 Promise 的函数也使其更简单
worker.on('message', async (data) => {
// note the "async" in the above
// the Promisified functions
const makeNewShift = (Email, Shifts) => new Promise((resolve, reject) => {
const newShift = new db.rota({ Email, Shifts });
newShift.save((err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
const updateShifts = shiftData => new Promise((resolve, reject) => shiftData.save((err) => {
if (err) {
reject(err);
} else {
resolve();
}
}));
const getShiftsForUser = email => new Promise((resolve, reject) => db.rota.getShiftsForUser(email, (err, shiftData) => {
if (err) {
reject(err);
} else {
resolve(shiftData);
}
}));
const checkUserHasShifts = email => new Promise(resolve => db.rota.checkUserHasShifts(email, resolve));
// Now on to the logic
console.log(data);
for (let i of data.entries) {
console.log(i.Email);
const flag = await checkUserHasShifts(i.Email);
if (flag) {
console.log('e', i.Email);
const shiftData = await getShiftsForUser(i.Email);
if (shiftData) {
shiftData.Shifts = i.Shifts;
await updateShifts(shiftData);
}
} else {
console.log('n', i.Email);
await makeNewShift(i.Email, i.Shifts);
}
};
console.log("Spreadsheet processed");
})
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.