如何让cursor.forEach()迭代器等待promise链解析?

时间:2016-12-14 08:06:21

标签: node.js mongodb node-mongodb-native

使用node.js mongodb本机模块我试图使用一个promisified迭代器,但它不会在解雇finalCallback

之前等待promise链解析
db.collection('items').find({}).forEach(promiseIterator, finalCallback);

function promiseIterator(doc){
    return Promise.resolve(doc)
        .then(res => {
            console.log(res); // this fires *after* finalCallback fires
        })
}

function finalCallback(res){
    console.log(res); // this fires *before* promiseIterator resolves all promise chains
}

文档在这里:https://mongodb.github.io/node-mongodb-native/2.2/api/Cursor.html#~resultCallback

他们的ES6示例都使用生成器,所以我不确定Promise是否在这里工作。但在解雇finalCallback之前,承诺链尚未完全解决。

https://mongodb.github.io/node-mongodb-native/2.2/reference/ecmascript6/crud/

我遇到此问题的原因是因为我的promiseIterator需要对每个文档进行多次异步/默认调用。 (我无法使用.toArray()将所有文档加载到内存中,因为有超过一百万个文档,并且在尝试时出现process out of memory错误。

2 个答案:

答案 0 :(得分:1)

不幸的是你做不到。 Mongodb原生的forEach并不等待Promise如您所注意到的那样解决。他们只是调用回调函数并继续下一个对象。

光标的forEach源代码(第769行)

http://mongodb.github.io/node-mongodb-native/2.2/api/lib_cursor.js.html

您需要使用nexthasNext手动迭代。

http://mongodb.github.io/node-mongodb-native/2.2/api/Cursor.html#next

编辑:添加了示例。

您可以完全按照jstice4all成功使用promises。这是他的例子,有更多细节。

db.collection('items').find({}, (err, resultCursor) => {
  resultCursor.next(processItem);

  function processItem(err, item) {
    if(item === null) return; // All done!

    // Add your asynchronous function/chain of functions in place of Promise.resolve()
    Promise.resolve().then(() => {
      resultCursor.next(processItem); // Read next item from database
    });
  }
});

当然,这会一个接一个地连续处理所有对象。如果你想并行处理所有对象并在做其他事情之前等待它们完成,你应该使用Promise.all收集所有创建的promises。

顺便说一句。不推荐使用nextObject,因此您可以使用next代替它。

答案 1 :(得分:1)

您可以使用nextObject代替forEach的递归:

db.collection('items').find({}, function(err, resultCursor) {
  function processItem(err, item) {
    if(item === null) {
      return; // All done!
    }

    externalAsyncFunction(item, function(err) {
      resultCursor.nextObject(processItem);
    });

  }

  resultCursor.nextObject(processItem);
}