Nested loop with synchronous and asynchronous behavior
First you can make the database container more precise and accessible if you define it like this:

var dbs = [
    { id: 'db1', docs: ['doc1', 'doc2', 'doc3'] },
    { id: 'db2', docs: ['doc4', 'doc5', 'doc6'] },
];


So you can access the database id via db.id and don't need to calculate it with dbId = Object.keys(dbObj)[0] 

Second the function's names aren't so good. The names dbIterator, docIterator and merchantIterator tell about HOW they do their work, but they should better tell WHAT they do as it is the case with getDocument.

Third in docIterator you are nesting promises but they are meant for chaining.

Fourth in docIterator the checks if (++docsProcessed &gt;= docsToBeProcessed) are needless because forEach makes sure that all documents will be processed and it will stop if there are no more.

When you are dealing with loops and asynchronous functions you should have a look at the Promise.all method. Provided that merchantIterator does realy nothing more than triggering the next iteration (as you suggested) I think the processing of all databases and documents may look like this:

function processDatabases(dbs) {
    Promise.all(dbs.map(db =&gt; {
        var connection = // some config for connection to **db.id**
        return Promise.all(db.docs.map(doc =&gt; {
            return getDocument(doc, connection)
                .then(doc =&gt; processDoc(doc, connection))
                .catch(err =&gt; logger.error(err.message));
        }));
    }))
    .then(() =&gt; logger.info('all done'));
}


In this case getDocument and processDoc should reject or throw an Error with an appropriate message.