在循环中等待长时间运行的代码块
Posted
技术标签:
【中文标题】在循环中等待长时间运行的代码块【英文标题】:Waiting on a long-running code block in a loop 【发布时间】:2020-05-09 04:49:21 【问题描述】:所以我想弄清楚为什么我的循环似乎没有按正确的顺序执行。这是工作线程在整理完值后进行的调用。
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")
)
对于一组 20 个条目,这将:
打印出 'data' 对象,数组中有 20 个Email:"someemail",Shifts:date:time
对象
按正确的顺序打印每封电子邮件
然后在列表中的最后一封电子邮件中执行 20 批 db 代码,在控制台中给我 20 行 n lastemail@example.com
和在数据库中的 20 个相同条目。
预期的行为显然是在继续下一个之前等待每个条目,或者至少为集合中的每封电子邮件排队一个条目。 如何让循环等待并正确执行所有条目的 db.rota.checkUserHasShifts 方法和回调,而不仅仅是最后一个?
【问题讨论】:
***.com/questions/750486/… 所以,本质上,您是在问如何在每次迭代中等待db.rota.checkUserHasShifts(i.Email,function(flag)
【参考方案1】:
您必须在调用 checkUserHasShifts
之前确定您的 i 变量的范围,否则它将被调用 20 次,并在您的循环中使用最后一个 i
值。
for (var i in items)
(function(i) db.rota.checkUserHasShifts(i)..)(i);
为了理解原因,我发现这篇优秀文章的第 6 节“重新审视范围”非常有用(尽管它针对的是来自 C# 背景的人):
https://mauricebutler.wordpress.com/tag/c-javascript/
相关引述:
JavaScript 没有“块作用域”,因此 for 循环没有引入新的作用域。这意味着每次访问元素变量时,都会更新相同的内存位置
这个SO问题也很好的展示了效果:JavaScript closure inside loops – simple practical example
【讨论】:
这很有效,而且是一个快速的修复,当场!【参考方案2】:解决这个问题的最简单方法是使用 Promises 和 async/await - 与使用回调检查是否还有更多要处理的内容相比,使代码非常干净
所以,首先,如果你“承诺”checkUserHasShifts
和 getShiftsForUser
函数,主循环非常简单!
此外,创建处理 .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");
)
【讨论】:
以上是关于在循环中等待长时间运行的代码块的主要内容,如果未能解决你的问题,请参考以下文章
java 多线程9 : synchronized锁机制 之 代码块锁