forEach 的简单承诺

Posted

技术标签:

【中文标题】forEach 的简单承诺【英文标题】:Simple promise with forEach 【发布时间】:2016-08-19 22:54:16 【问题描述】:

我正在尝试使用 Promise 来等待异步 forEach:

var forEachMatch = function(matches) 
    var deferred = Q.defer();
    matches.forEach(function(match) 
        var messages = Message.find('matchid': match._id, function(err,messages) 
            if(err)
                console.log(err);
            else
                console.log(match._id);
        );
    );
    return deferred.promise;
;

在这里使用:

forEachMatch(matches, function() 
    console.log("DONE");
    res.status(200).json(
        bob,
        matches: matches,
    );
);

我的控制台输出如下:所有match._id 都已打印,但DONE 从未打印。

有解决办法吗?我从 node 和 promises 开始,所以我肯定会忘记一些东西,但我看不到什么。

感谢您的任何回答。

编辑

最终的解决方案,感谢 Alexander Mac:

var forEachMatch = function(matches) 
  var promises = matches.map(match => 
    return Message
    .find('matchid': match._id)
    .populate(
      path: 'fromUser toUser',
      select: 'id firstname'
    )
    .then(messages => [match, messages])
    .catch(err => 
      console.log("Error in forEachMatch");
      return [match, null];
    );
  );
  return Q.all(promises);


forEachMatch(matches)
  .then(messagesByMatch => 
    res.status(200).json(
      user,
      messagesByMatch: messagesByMatch
    );
  );

【问题讨论】:

你的 forEachMatch 方法只接受一个参数,所以你的 DONE 回调永远不会被调用 【参考方案1】:

在您的情况下,最好使用Q.all,它接受承诺或值的数组:

var forEachMatch = function(matches) 
  var promises = matches.map(match => 
    return Message
      .find('matchid': match._id)
      .then(messages => [match, messages]);
  );
  return Q.all(promises);


forEachMatch(matches)
  .then(results => 
    console.log("DONE", results);
    res.status(200).json(
      bob,
      matches: matches,
  );
);

https://github.com/kriskowal/q/wiki/API-Reference#promiseall

【讨论】:

这种解决方案是否可以在 .map 函数结束时返回数据?我在 matches.map 的每次迭代中处理一个变量,并希望在最后返回它。 是的,这是可能的,但是您想要返回什么?所有match._id? 请查看我对您答案的编辑,我想处理错误并返回结果。 请将其添加为评论。 var forEachMatch = function(matches) var result = []; var promises = matches.map(match => Message.find('matchid': match._id, function(error, messages) if(error) // return error else result.push([match,messages]); )); return Q.all(promises); 【参考方案2】:

您正在返回一个延迟的承诺,并且该承诺没有得到解决或拒绝。请尝试以下操作:

var forEachMatch = function(matches) 
var deferred = Q.defer();
matches.forEach(function(match) 
    var messages = Message.find('matchid': match._id, function(err,messages) 
        if(err)
            deferred.reject(err);
        else
            deferred.resolve(match._id);
    );
);
return deferred.promise;

;

调用者会变成

    forEachMatch(matches).then(id=>

     console.log("done succesffuly found");

   ).catch(err=>
         console.log("not found error",err);
   ).done(()=>
         console.log("done")
   );

【讨论】:

.then.done 闭包有什么区别? .then 闭包是否在 matches.forEach 的每次迭代中调用?【参考方案3】:

您从未调用过promise.rejectpromise.reject 方法。此外,对异步操作做出了承诺,而 forEach 则没有。

这是一个肮脏的修复:

 var forEachMatch = function(matches) 
  var deferred = Q.defer();
  // make the foreach asynchronous
  setTimeout(function() 
    matches.forEach(function(match) 
      // do stuff
    );
    // notify end
    deferred.resolve();
  , 0);

  return deferred.promise;
;

// use .then
forEachMatch(matches).then(function() 
  console.log("DONE");
);

【讨论】:

那么在执行其他操作(例如发送请求结果)之前等待我的 forEach 结束的最佳方法是什么?

以上是关于forEach 的简单承诺的主要内容,如果未能解决你的问题,请参考以下文章

如何与 forEach 同时执行承诺?

在承诺完成之前退出 forEach

挣扎于js承诺,foreach承诺添加结果。

Angular forEach 等到所有承诺都完成?

带有多个猫鼬查找的foreach循环后的javascript承诺

使 angular.forEach 在转到下一个对象后等待承诺