【问题标题】: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 方法和回调,而不仅仅是最后一个?

【问题讨论】:

标签: javascript mongodb asynchronous mongoose async-await


【解决方案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");
})

【讨论】:

    猜你喜欢
    • 2012-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-10
    • 2021-03-16
    • 1970-01-01
    • 2021-10-22
    • 2021-07-28
    相关资源
    最近更新 更多