【问题标题】:Promises and Q - Completing a long series of functions with a forEachPromises 和 Q - 使用 forEach 完成一系列功能
【发布时间】:2016-05-26 08:38:55
【问题描述】:

对如何最好地写这个有点困惑 - 希望这个描述清楚。

我有一个 forEach 函数,我在其中遍历一些 JS 对象数据,并为每个项目执行一个函数:

found.forEach(function(item) {
      processData(item['userID']);
});

在这个 processData 函数中,我正在使用 MongoDB find() 调用。

var processData = function(userIDSelected) {

    User.find( {_id: userIDSelected},
           {gender: 1, country:1}, function(req, foundUser) {
            processUserInfo(foundUser[0]['gender']);
    });
}

我遇到的问题是,考虑到每个调用将依次运行processUserInfo,我如何等待 forEach 中的所有内容完成。

我已经研究过使用 Q 库和 Q.all,但是这不起作用。

是否有 Q 函数等待长链中的所有内容完成?

谢谢

【问题讨论】:

  • 你能不能创建另一个函数来接收ObjectId 列表来一次检索所有用户?

标签: javascript mongodb promise q


【解决方案1】:

按照您的编码方式,我可以假设 userID 是来自 MongoDB 的 ObjectId

如果是这种情况,只要found 不为空,这将起作用,否则您的用户将永远等待服务器响应。

processData(
  // Retrieve an object 
  // {
  //   $in: [ObjectId, ObjectId, ...]
  // }
  // 
  found.reduce(
    function(query, user) {
      return query.$in.push(user.userID), query;
    },
    {$in: []}
  )
);

您可以了解有关$in 运算符here 的更多信息。

【讨论】:

    【解决方案2】:

    Q.all:

    返回一个通过包含每个 Promise 的实现值的数组来实现的 Promise,或者以与第一个被拒绝的 Promise 相同的拒绝原因被拒绝。

    Q.allSettled:

    返回一个通过一系列 Promise 状态快照实现的 Promise,但只有在所有原始 Promise 都已解决之后,即成为已实现或被拒绝。

    所以你会做三件事:

    1. 修改 processData 并可能修改您对 MongoDB 的调用,以便您最终以 processData 返回异步操作的承诺。 (抱歉,我不熟悉 MongoDB。Alnitak says 在现代版本中,如果您不提供回调,MongoDB 会返回一个承诺(很好!)。否则,this question and its answers may help 会返回一个回调的承诺-基于 API。)

    2. 使用map 而不是forEach 来获取结果承诺的数组。

    3. 在该承诺数组上使用 Q.allQ.allSettled

    如果User.find 在没有指定回调的情况下返回一个promise,那么#1 看起来像这样something

    var processData = function(userIDSelected) {
    
        return User.find(
               {_id: userIDSelected},
               {gender: 1, country:1}
        ).then(function(req, foundUser) { // <== Check these args with the MongoDB docs!
            return processUserInfo(foundUser[0]['gender']);
        });
    };
    

    如果没有,你可以自己用Q.defer

    var processData = function(userIDSelected) {
        var d = Q.defer();
        User.find(
               {_id: userIDSelected},
               {gender: 1, country:1},
               function(req, foundUser) {
            processUserInfo(foundUser[0]['gender']);
            d.resolve(/*...data could go here...*/); // You'd use d.reject() if there were an error
        });
        return d.promise;
    };
    

    然后是 2 和 3 的样子:

    Q.all(found.map(function(item) { // Or Q.allSettled
        return processData(item);
    }))
    .then(...)
    .catch(...);
    

    如果 processData 只使用它的第一个参数(忽略任何额外的参数),您可以放弃中间函数:

    Q.all(found.map(processData)) { // Or Q.allSettled
    .then(...)
    .catch(...);
    

    ...但如果processData 忽略额外的参数,因为map 将传递它三个(值、它的索引和数组)。

    【讨论】:

    • OP 还需要让 MongoDB .find 调用返回一个承诺而不是使用回调
    • 好的 - 你能再描述一下吗?因为我只想让另一个函数在找到后运行
    • @userMod2:我根据 Alnitak 的评论扩展了一些内容。
    • @T.J.Crowder - 我在原来的问题中打错了,processDataOptions 应该是 processData。已更新代码
    • @T.J.Crowder 如果没有传递回调函数,最新的 Node MongoDB API 将自动返回 Promise
    猜你喜欢
    • 1970-01-01
    • 2020-02-25
    • 2013-09-28
    • 1970-01-01
    • 2014-07-03
    • 2015-02-10
    • 2019-11-01
    • 2016-02-22
    • 2022-08-15
    相关资源
    最近更新 更多