Ожидание долго выполняющегося блока кода в цикле - PullRequest
1 голос
/ 23 января 2020

Так что я пытаюсь обернуть голову, почему мой l oop не работает в правильном порядке. Это вызов, выполняемый рабочим потоком после завершения сортировки значений.

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 записей это будет:

  • Распечатать «данные» object out, который содержит 20 {Email:"someemail",Shifts:{date:time}} объектов в массиве
  • Распечатывает каждое электронное письмо в правильном порядке
  • Затем выполняет 20 лотов кода БД для последнего электронного письма в списке, давая мне 20 строк n lastemail@example.com в консоли и 20 идентичных записей в базе данных.

Очевидно, что предполагаемое поведение состоит в том, чтобы ждать каждой записи перед переходом к следующей или, по крайней мере, ставить в очередь запись для каждого письма в наборе. Как заставить l oop ждать и правильно выполнить метод db.rota.checkUserHasShifts и обратный вызов для всех записей, а не только для последней?

Ответы [ 2 ]

1 голос
/ 23 января 2020

Вы должны указать переменную i перед вызовом checkUserHasShifts, иначе она будет вызвана 20 раз с последним значением i в вашем l oop.

foreach (var i in items){
 (function(i){ db.rota.checkUserHasShifts(i)..})(i);
}

, чтобы понять, почему я нашел раздел 6 «Пересмотренная область» этой превосходной статьи очень полезным (хотя он нацелен на людей, происходящих из C# фона):

https://mauricebutler.wordpress.com/tag/c-javascript/

Соответствующая цитата:

JavaScript не имеет "Block Scope", поэтому для l oop не вводится новая область. Это означает, что при каждом доступе к переменной элемента обновляется одна и та же ячейка памяти

Этот вопрос SO также очень хорошо демонстрирует эффект: JavaScript замыкание внутри циклов - простой практический пример

0 голосов
/ 23 января 2020

Самый простой способ сделать это с помощью Promises и async / await - делает код очень чистым по сравнению с использованием обратных вызовов, которые проверяют, есть ли еще что-то для обработки

Итак, во-первых, если вы «обещаете» * Функции 1003 * и getShiftsForUser, основные l oop удивительно просты!

Кроме того, создание функций, которые обрабатывают .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");
})
...