对一组元素做多个promise

时间:2016-12-17 23:28:57

标签: javascript node.js mongoose promise bluebird

我有一个objectIds列表,我想转到不同的集合,并根据每个Id进行操作。我宁愿一个接一个地执行操作(顺序)

var removeOperation = function(objectified){
   return Comps.findOne({reviews : objectified}).populate([{ path: "reviews", match : {_id : objectified}}])

}
var firstCheckIfAnonHasTheIdInReviewsArrayIfThereDeleteIt = function(objectified){
    var query = {reviews : objectified};
    var update = {$pull : {reviews : objectified}};
    var option = {new :true};
    return Anon.findOneAndUpdate(query, update, option );
};
var thenCheckIfUserHasTheIdInReviewsArrayIfThereDeleteIt = function(objectified){
    var query = {reviews : objectified};
    var update = {$pull : {reviews : objectified}};
    var option = {new :true};
    return User.findOneAndUpdate(query, update, option );
}

我走这条路:

Promise.mapSeries(arrOfObjectIds, function(e){
    return removeOperation(e);
})
.then(function(results){
    console.log(results);
    var map = results.map(function(e){
        // return e.reviews[0]
        return e

    })
    console.log("map : ", map)
    return Promise.resolve(map);
})
.then(function(compDocs){
    console.log("compDocs: ",compDocs)
    Promise.mapSeries(compDocs,  function(compDoc){
        return updateCompAndRemoveReviewFromArray(compDoc) // I know it's not show. It's another promise I use
    })

}).then(function(returned){
    return Reviews.remove({_id : {$in : arrOfObjectIds }})
})
.then(function(){
  I wanted to do firstCheckIfAnonHasTheIdInReviewsArrayIfThereDeleteIt on the array of object Ids to delete the review from the array. Also if we succesfully removed the array here we should not have to go to the next user 
  promise which deletes a users review since if we deleted in Anon it won't be in User. since there is only one review ID possible per review.
})
.then(function(){
    //if there was no review pulled from the Anon reviews Array. that means it's in the users review and we should do this promise 

       thenCheckIfUserHasTheIdInReviewsArrayIfThereDeleteIt()
})

所以也许你可以告诉我如何在一个元素数组上使用mapSeries,这样它就不会有一个承诺但会做多个承诺。

我们可以这样做:

Promise.mapSeries(arrOfObjectIds, function(e){
    return removeOperation(e);
    return firstCheckIfAnonHasTheIdInReviewsArrayIfThereDeleteIt(e)// extra credit: check if this was successful (review was pulled). If it wasn't got to next one.
    return thenCheckIfUserHasTheIdInReviewsArrayIfThereDeleteIt(e)
})

3 个答案:

答案 0 :(得分:1)

以简化的术语重述问题:

  

您有一系列ID,而且您想依次为每个ID调用三个承诺返回函数,ABC,如下所示:

     
      
  • A(id)(无条件地)
  •   
  • 然后B(id)(无条件地)
  •   
  • 然后C(id)(有条件地,取决于B(id)的结果)
  •   
  

我们可以这样做:

Promise.mapSeries(arrOfObjectIds, function(e){
    return removeOperation(e);
    return firstCheckIfAnonHasTheIdInReviewsArrayIfThereDeleteIt(e)// extra credit: check if this was successful (review was pulled). If it wasn't got to next one.
    return thenCheckIfUserHasTheIdInReviewsArrayIfThereDeleteIt(e)
})

是的,虽然不像建议的代码那样

首先,您有一个关于B报告其结果的方式的设计选择。这个问题暗示B的结果是成功"成功" vs"失败",但这不是建模的唯一方法。

选项1:沿着承诺链的成功路径传递的测试数据

B,使其返回的承诺在成功(Anon审核被删除)或预期失败(Anon审核未被删除)时同时实现,并通过以下方式报告结果参数的意思。

var B = function(objectified) {
    var query = {reviews: objectified};
    var update = {$pull: {reviews: objectified}};
    var option = {new :true};
    return Anon.findOneAndUpdate(query, update, option).exec();
};

然后你会写:

Promise.mapSeries(arrOfObjectIds, function(id) {
    return A(id).then(function() {
        return B(id);
    }).then(function(item) { // item will be `null` if B(id) found nothing.
        return item || C(id);
    }).catch(function(error) {
        // If anything went wrong, catch the error and log it.
        console.log(error); 
        // By not re-throwing the error, the mapseries() is allowed to continue.
    });
});

选项2:在承诺链的失败路径下传递测试错误

B以使其返回的承诺在成功时履行,或在预期的失败时拒绝。

var B = function(objectified) {
    var query = {reviews: objectified};
    var update = {$pull: {reviews: objectified}};
    var option = {new :true};
    return Anon.findOneAndUpdate(query, update, option).exec().then(function(item) {
        return item || Promise.reject(new Error('not found'));
    });
};

然后你会写:

Promise.mapSeries(arrOfObjectIds, function(id) {
    return A(id).then(function() {
        return B(id).catch(function(error) {
            // Here, you have to discriminate between the "expected error" and any unexpected errors.
            if(error.message === 'not found') {
                return C(id);
            } else {
                throw error; // unexpected error - rethrow it
            }
        });
    }).catch(function(error) {
        // If anything went wrong, catch the error and log it.
        console.log(error); 
        // By not re-throwing the error, the overall mapseries() is allowed to continue.
    });
});

在两个选项中:

  • 要返回真正的承诺,请在.exec()AB中使用C。 (据我了解Mongoose,没有exec()你得到的东西有.then()方法,但这不是一个完整的Promise)。
  • 如果您希望整个序列在第一个错误时停止,请在记录后重新抛出错误,或者完全省略最终的catch()
  • 在条件阶段之前或之后,可以非常简单地添加其他无条件阶段。

对我来说,选项2更符合逻辑,但我可能会选择选项1,因为它更加简单和高效。

答案 1 :(得分:0)

您可以使用Array.reduce()来执行您的承诺:

arrOfObjectIds.reduce(function(promise, objectId) {
  return promise.then(function(result) {
    return removeOperation(objectId)
      .then(firstCheckIfAnonHasTheIdInReviewsArrayIfThereDeleteIt)
      .then(thenCheckIfUserHasTheIdInReviewsArrayIfThereDeleteIt);
  });
}, Promise.resolve());

这将一次执行数组中removeOperation -> firstCheck.. -> thenCheck个项目的链,然后移动到下一个项目。

答案 2 :(得分:0)

can we doe something like:是的,除此之外,第一个返回退出函数

所以,你可能会做类似

的事情
Promise.mapSeries(arrOfObjectIds, function(e){
    return removeOperation(e)
    .then(function() {
        return firstCheckIfAnonHasTheIdInReviewsArrayIfThereDeleteIt(e);
    }).then(function() {
        return thenCheckIfUserHasTheIdInReviewsArrayIfThereDeleteIt(e);
    })
})