2015-06-14 2 views
0

У меня есть функция, которую я хочу решить только после завершения foreach .. im using q и mongoose, на что ссылаются капитализированные вещи, и в основном я хочу создать набор элементов, затем запустите функцию после завершения foreach.Возвращение q разрешено, когда все операции завершены

function createItems() { 

    var deferred = Q.defer(); 

    var itemsArray = [{ 'name' : 'spade' }, { 'name' : 'bucket' } , { 'name' : 'sand'}]; 

    itemsArray.forEach(function(itemObj) { 
    var item = new Item(itemObj); // forces it to use a schema (dont worry) 
    Item.findOneAndUpdate({ 
     url: item.short_name 
    }, item, { 
     upsert: true 
    }, function(err) { 
     if (!err) { 
     console.log(item.name + ' created.'); 
     deferred.resolve(); 
     } else { 
     deferred.reject(new Error(err)); 
     } 
    }); 
    }); 


    return deferred.promise; 
} 

createItems() 
    .then(function() { 
     console.log('All items done.'); 
    }); 

Так что я бы ожидал увидеть на консоли что-то вроде;

созданный объект spade bucket item созданный sand item created Все готово.

ответ

3

Быстрая сторона: в вашем массиве есть проблема с синтаксисом, он содержит один объект с несколькими попытками записи name. Я предположил, что вы намеревались иметь массив объектов, каждый из которых имеет name.

В принципе, ваш подход терпит неудачу, потому что вы решаете свое обещание в самом первом экземпляре. Вам нужно обещание, которое представляет собой несколько вызовов асинхронных вызовов. Стандартный способ сделать это - передать массив обещаний в библиотечную функцию, такую ​​как Q.all/Bluebird.all/Promise.all. Функция all принимает массив обещаний и возвращает обещание для массива результатов. Он решает, когда все обещания разрешаются, или он отвергает, когда отвергается какое-либо из обещаний.

function createItems() { 

    var promisesForUpdatedDocs = []; 

    var itemsArray = [ 
    { name: 'spade'}, 
    { name: 'bucket'}, 
    { name: 'sand'} 
    ]; 

    itemsArray.forEach(function(itemObj) { 
    var item = new Item(itemObj); 
    var updatedDocPromise = Item.findOneAndUpdate({ 
     url: item.short_name 
    }, item, { 
     upsert: true 
    }).exec(); // returns an mPromise 
    promisesForUpdatedDocs.push(Q(updatedDocPromise)); // using Q to turn the mPromise to a Q promise, and adding it to an array 
    }); 

    return Q.all(promisesForUpdatedDocs); // creates a promise for array of results (from array of promises) 
} 

createItems() 
    .then(function(items) { 
    console.log('All items done:', items); 
    }).catch(console.log); // never forget to handle errors! Always `return` or `catch` a promise chain. 
+0

Предполагая, что 'Item.findOneAndUpdate' - это асинхронная функция, которую вы фактически вызываете' Q.all' с пустым массивом. функция 'findOneAndUpdate' сама должна быть завернута в/вернуть объект обещания, который вы нажимаете на массив, а затем имеет значение для возврата' Q.all' –

+0

D'oh! Вы, конечно, правы. Я отредактирую. > _ < –

+0

EDIT: и сделано. 'findOneAndUpdate' возвращает запрос Mongoose, который может быть' exec() ''d, чтобы вернуть обещание. –

Смежные вопросы