简体   繁体   English

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

[英]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 个条目,这将:

  • Print the 'data' object out, which has 20 {Email:"someemail",Shifts:{date:time}} objects in an array打印出 'data' 对象,它在一个数组中有 20 个{Email:"someemail",Shifts:{date:time}}对象
  • Print each email out, in the right order按正确的顺序打印每封电子邮件
  • Then execute 20 lots of the db code on the last email in the list, giving me 20 lines of n lastemail@example.com in console and 20 identical entries in the database.然后在列表中的最后一封电子邮件上执行 20 批 db 代码,在控制台中给我 20 行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!因此,首先,如果您“承诺” checkUserHasShiftsgetShiftsForUser函数,主循环非常简单!

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM