nodejs и mongoskin, обратный вызов после сохранения всех элементов

У меня есть следующий фрагмент кода, где я перебираю коллекцию, делаю еще один запрос БД и создаю объект внутри его обратного вызова. Наконец я сохраняю этот объект в другой коллекции.

Я хочу вызвать другую функцию после сохранения всех элементов, но не могу понять, как это сделать. Я попытался использовать библиотеку async, в частности, async, когда item не равен null, но это просто заталкивает меня в бесконечный цикл.

Есть ли способ определить, когда все элементы были сохранены?

Спасибо!

var cursor = db.collection('user_apps').find({}, {timeout:false});
cursor.each(function (err, item) {
    if (err) {
        throw err;
    }
    if (item) {
        var appList = item.appList;
        var uuid= item.uuid;
        db.collection('app_categories').find({schema_name:{$in: appList}}).toArray(function (err, result) {
            if (err) throw err;
            var catCount = _.countBy(result, function (obj) {
                return obj.category;
            })
            catObj['_id'] = uuid;
            catObj['total_app_num'] = result.length;
            catObj['app_breakdown'] = catCount;
            db.collection('audiences').insert(catObj, function (err) {
                if (err) console.log(err);
            });
        }); 
    }
    else {
        // do something here after all items have been saved
    }
});

1 ответ

Решение

Ключевым моментом здесь является использование чего-то, что будет соответствовать сигналу обратного вызова при выполнении операции "цикл". .each() как реализовано здесь, этого делать не нужно, поэтому вам нужен элемент управления "асинхронный" цикл, который будет означать, что каждый цикл повторен и завершен, со своим собственным обратным вызовом в обратном вызове.

Если ваш базовый драйвер MongoDB имеет версию не ниже 2, то есть .forEach() который имеет обратный вызов, который вызывается, когда цикл завершен. Это лучше чем .each(), но это не решает проблему знания, когда внутренний "асинхронный" .insert() операции были завершены.

Поэтому лучше использовать интерфейс потока, возвращаемый .find() где это более разрешено управление потоком. E сть .stream() метод обратной совместимости, но современные драйверы просто возвращают интерфейс по умолчанию:

var stream = db.collection('user_apps').find({});

stream.on("err",function(err){
    throw(err);
});

stream.on("data",function(item) {
    stream.pause();                 // pause processing of stream
    var appList = item.appList;
    var uuid= item.uuid;
    db.collection('app_categories').find({schema_name:{ "$in": appList}}).toArray(function (err, result) {
        if (err) throw err;
        var catCount = _.countBy(result, function (obj) {
            return obj.category;
        })

        var catObj = {};        // always re-init
        catObj['_id'] = uuid;
        catObj['total_app_num'] = result.length;
        catObj['app_breakdown'] = catCount;
        db.collection('audiences').insert(catObj, function (err) {
            if (err) console.log(err);
            stream.resume();        // resume stream processing
        });
    }); 
});

stream.on("end",function(){
    // stream complete and processing done
});

.pause() Метод в потоке останавливает дальнейшие события, так что каждый результат объекта обрабатывается по одному. Когда обратный звонок от .insert() называется, то .resume() вызывается метод, означающий, что обработка этого элемента завершена, и для обработки следующего элемента может быть сделан новый вызов.

Когда поток завершен, тогда все сделано, поэтому для продолжения вашего кода вызывается ловушка события end.

Таким образом, оба цикла обозначаются концом для перехода к следующей итерации, а также определенным событием "конец" для полного завершения обработки. Поскольку контроль находится "внутри" .insert() обратный вызов, то эти операции также выполняются.

В качестве дополнительного примечания вы можете рассмотреть возможность включения информации о вашей "категории" в исходную коллекцию, поскольку, вероятно, ваши результаты могут быть более эффективно возвращены с помощью .aggregate() если все необходимые данные были в одной коллекции.

Другие вопросы по тегам