在循环中等待长时间运行的代码块

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 - 与使用回调检查是否还有更多要处理的内容相比,使代码非常干净

所以,首先,如果你“承诺”checkUserHasShiftsgetShiftsForUser 函数,主循环非常简单!

此外,创建处理 .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");
)

【讨论】:

以上是关于在循环中等待长时间运行的代码块的主要内容,如果未能解决你的问题,请参考以下文章

synchronized锁机制 之 代码块锁(转)

java 多线程9 : synchronized锁机制 之 代码块锁

PLSQL 块需要很长时间来声明游标

在执行下一个代码块之前等待多个 RxJS 调用

在 OpenMP 中,我们如何并行运行多个代码块,其中每个代码块包含 omp single 和 omp for 循环?

Java多线程5:synchronized锁方法块