amdc
amdc

Reputation: 530

promised-mongo: can't finalize promise

Here's a piece of code I'm working at: it saves reddit posts to mongoDB collection.

I'm using promised-mongo library

The problem is when the for loop completes and all data is saved to database, program does not exit, it continues executing doing nothing, despite of done() called at the end of each promised-mongo promise chain.

    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();
    }

What am I doing wrong?

Upvotes: 0

Views: 562

Answers (1)

JLRishe
JLRishe

Reputation: 101738

There are a few problems here:

  • You are creating several promises in a for loop, but not keeping track of them
  • You have a DB.posts.insert that creates a promise, but you are not awaiting it

To address them in reverse order:

If you don't return the promise created by DB.posts.insert there will be no way of awaiting it until it is done. You need to return it:

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

And you also need to keep track of all the promises you are creating, so that you know when they are all done. An easy way to do this is to use .map() to map them to an array of promises, and then use Promise.all() to await them.

Assuming that posts is an array:

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
});

This also eliminates the need for that unpleasant IIFE you had there.

Upvotes: 3

Related Questions