【问题标题】:promised-mongo: can't finalize promise承诺的mongo:无法完成承诺
【发布时间】:2015-12-16 00:18:40
【问题描述】:

这是我正在处理的一段代码:它将 reddit 帖子保存到 mongoDB 集合中。

我正在使用promised-mongo 库

问题是当for循环完成并且所有数据都保存到数据库时,程序没有退出,它继续执行什么都不做,尽管done()在每个promise-mongo承诺链的末尾被调用。

    for (var i = 0; i< posts.length; i++) { 
        posts[i].done = false;
        DB.posts.findOne({
            "id" : posts[i].id // it's 'id', not mongo's '_id'
        })
        .then(function(i) {
            return function(doc){
                if(doc) {
                    console.log('skipping')
                } else {
                    DB.posts.insert(posts[i]).then(function() {
                        console.log(arguments);
                        nSaved++;
                    });
                }
            }
        }(i))
        .catch(function(){
            console.log(arguments)
        })
        .done();
    }

我做错了什么?

【问题讨论】:

  • 为什么你认为程序需要在“for 循环完成时”退出?仍然有很多打开的回调,每个回调都会触发另一个异步操作 (insert),并带有自己的回调。
  • 这是你的全部程序吗? nSaved++ 有什么用?你得到什么输出?你为什么打电话给done()?
  • 在您关闭MongoClient 连接池(所有更新完成后)之前,您的 node.js 程序不会关闭。你是在哪里做的吗?
  • @JohnnyHK 正确!我忘了关闭连接。但是,我不知道如何正确等待 batch job 完成。我更改了我的代码并添加了一个then,它评估nSaved+nSkipped === nPlanned,如果它是真的则关闭连接;有什么方法可以做得更好?
  • @amdc 您可以使用Q.all 或Q.allSettled 来协调在一组承诺得到履行后采取的行动。见here。

标签: javascript node.js mongodb promise


【解决方案1】:

这里有几个问题:

  • 您在 for 循环中创建了多个 Promise,但没有跟踪它们
  • 你有一个 DB.posts.insert 来创建一个承诺,但你没有在等待它

以相反的顺序解决它们:

如果您不返回由DB.posts.insert 创建的承诺,则在完成之前将无法等待。您需要退货:

return function(doc){
    if(doc) {
        console.log('skipping')
    } else {
        // here
        return DB.posts.insert(posts[i]).then(function() {
            console.log(arguments);
            nSaved++;
        });
    }
}

您还需要跟踪您正在创建的所有承诺,以便您知道它们何时完成。一个简单的方法是使用.map() 将它们映射到一组promise,然后使用Promise.all() 等待它们。

假设posts是一个数组:

function ensurePost(post) {
    post.done = false;

    return DB.posts.findOne({
        "id" : post.id // it's 'id', not mongo's '_id'
    })
    .then(function(doc){
        if(doc) {
            console.log('skipping ' + post.id)
        } else {
            return DB.posts.insert(post).then(function() {
                console.log(arguments);
                nSaved++;
            });
        }
    })
    .catch(function(error){
        console.error('Error inserting', post.id, error);
    });
}

Promise.all(posts.map(ensurePost))
.done(function () {
    // all done. close the connection
});

这也消除了您在那里遇到的不愉快的 IIFE 的需要。

【讨论】:

  • 非常感谢!最初的问题由 cmets 中的 @JohnnyHK 解决,但我需要这个反馈。我仍然不完全理解承诺,但它有点帮助:)
猜你喜欢
  • 2016-03-27
  • 1970-01-01
  • 1970-01-01
  • 2015-10-06
  • 2017-06-17
  • 1970-01-01
  • 2018-06-23
  • 1970-01-01
相关资源
最近更新 更多