Pål Thingbø Pål Thingbø - 6 months ago 37
Node.js Question

Is there a simpler way to chain promises from MongoDB results?

This function lists all collections in a MongoDB database, with the number of documents in each collection (bluebird promises).

function listMongoCollections(db) {
var promises = []
db.listCollections().toArray().then((docs) => {

docs.forEach((doc) => {

promises.push(
new Promise((resolve) => {
db.collection(doc.name).count().then((count) => {
doc.count = count
resolve()
})
})
)
})

return Promise.all(promises)
})
}


Is there a simpler way to do this? Using this method will flood an app with code, and I haven't even included error handling.

Answer

You can do a few things:

  • use .map to transform an array of collections into an array of promises, instead of .pushing into an array manually.
  • avoid wrapping promises in new Promises.
  • nest .then as little as possible. In the following example, notice how the first part just returns an array of promises. Only before returning the main function we wrap it in an .all promise.
function listMongoCollections(db) {
    const docs = db.listCollections().toArray().then(docs => {
        return docs.map(doc => {
            return db.collection(doc.name).count().then(count => {
                doc.count = count
                return doc;
            });
        });
    });
    return Promise.all(docs);
}

You can "simplify" it further by removing the returns and the intermediate docs constant.

function listMongoCollections(db) {
    return Promise.all(
        db.listCollections().toArray().then(docs => 
            docs.map(doc => 
                db.collection(doc.name).count().then(count => {
                    doc.count = count
                    return doc;
                })
            )
        )
    );
}

And perhaps with async/await we can make it even easier to read (although I'm not too familiar with it):

async function listMongoCollections(db) {
    let docs = await db.listCollections().toArray();
    docs = docs.map(async doc => {
        doc.count = await db.collection(doc.name).count();
        return doc;
    });
    return Promise.all(docs);
}

These are just examples, Mongo might offer even better solutions.