如何在带有bluebird的nodejs的循环中处理链式promise

时间:2018-02-04 11:26:07

标签: node.js loops promise bluebird chain

问题的要点是:

for (let i=0;i<list.length;i++) {
    AsyncCall_1({'someProperty': list[i] })
    .then((resp_1) => {
        resp_1.doSomething();
        resp_1.AsyncCall_2()
        .then((resp_2) => {
            resp_2.doSomething();
        })
  })
}

after last resp.AsyncCall_2.then(()=> {
//do something
})

我需要按顺序链接所有的promises,以便循环等待“resp.AsyncCall_2”函数在下一次迭代时被解析。在最后一次“resp.AsyncCall_2”调用之后做了一些事情。 (因为所有的承诺都将得到解决)

实际问题:

for (var i=0;i<todo.assignTo.length;i++) {
    Users.findOne({'username': todo.assignTo[i] })
    .then((user) => {
         user.assigned.push(todo.title);
         user.notificationCount.assignedTodosCount++;
         user.save()
         .then((user) => {
             console.log("todo is assigned to the user: " + user.username)
         })
     })
}


//to something at last call resloved (I know this is wrong way of doing this)
Users.find({})
.then((users)=> {
    var promises = [];
    for (var i=0;i<users.length;i++) {
        users[i].notificationCount.totalTodosCount++;
        promises.push(users[i].save());
    }
    Promise.all(promises)
    .then(()=> {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'application/json');
        console.log("todo is successfully posted");
        res.json({success : true, todo});
    },(err) => next(err))
.catch((err) => next(err));
})

提前感谢你..

2 个答案:

答案 0 :(得分:1)

在node.js的现代版本中,您只需使用async/await并且不需要使用Bluebird迭代函数:

async function someMiddlewareFunc(req, res, next) {
    try {
        for (let item of list) {
            let resp_1 = await AsyncCall_1({'someProperty': item });
            resp_1.doSomething();
            let resp_2 = await resp_1.AsyncCall_2();
            resp_2.doSomething();
        }
        // then do something here after the last iteration 
        // of the loop and its async operations are done
        res.json(...);
    } catch(err) {
        next(err);
    }
}

这将序列化操作(这是你要求的),因此循环的第二次迭代不会开始,直到第一次迭代中的异步操作完成。

但是,它并不会出现在您的实际代码中,您实际需要序列化单个操作并序列化不必序列化的内容通常会使端到端时间完成它们更长。因此,您可以并行运行循环中的所有项目,最后收集所有结果,然后发送您的响应,Bluebird的Promise.map()对此非常有用,因为它结合了{{1和.map()成一个函数调用:

Promise.all()

仅供参考,使用function someMiddlewareFunc(req, res, next) { Promise.map(list, (item) => { return AsyncCall_1({'someProperty': item}).then(resp_1 => { resp_1.doSomething(); return resp_1.AsyncCall_2(); }).then(resp_2 => { return resp_2.doSomething(); }); }).then(results => { // all done here res.json(...); }).catch(err => { next(err); }); } 时,您无需设置这些res.json(...)res.statusCode = 200;,因为它们会自动为您完成。

关于蓝鸟res.setHeader('Content-Type', 'application/json');的进一步说明。它接受一个Promise.map()选项,告诉Bluebird允许在飞行中运行多少次操作&#34;同时。默认情况下,它会同时并行运行它们,但您可以传递任何所需的数字作为并发选项。如果您通过{concurrency: n},它将序列化事物。在允许并行操作时使用此选项特别有用,但是数组非常大并且并行地迭代所有这些选项会遇到内存使用问题或者压倒目标服务器。在这种情况下,您可以将1值设置为某个中间值,该值仍然可以为您提供一些并行执行的度量,但不会超过目标(5到20之间的某个数字通常是合适的 - 它取决于在目标服务上)。有时,商业服务(如谷歌)也会限制他们将从同一IP地址同时处理多少请求(以保护他们一次使用过多服务来保护他们)和concurrency因为这个原因,价值也很有用。

答案 1 :(得分:0)

您是否尝试过Promise.each

const users = todo.assignTo.map(function(user) {
   return Users.findOne({'username': assigned_to });        
}
Promise.each(users, function(user) {
   user.assigned.push(todo.title);
   user.notificationCount.assignedTodosCount++;
   user.save()
   .then((user) => {
      console.log("todo is assigned to the user: " + user.username)
   })
}