Search code examples
javascriptmongodbbluebird

Bluebird map returning early


I am using Bluebird.map but it is returning my array of objects early for some reason:

function returnInvoicePrice(items) {
    return Bluebird.map(items, function(item) {
        db.collection('products').find({
            product_code    : item.product_code
        }).toArray(function(err, product) {
            var invoice_price = product[0].invoice_price;
            console.log('--product--');
            console.log(product);
            return product;
        });
    }).then(function(items) {
        console.log('--items w/ invoice price--'); // this prints BEFORE my console.log(product) for some reason...
        console.log(items);
    }).catch(function(error) {
       console.log('--bluebird error in returning price for each item--');
       console.log(error);
    });
}

returnInvoicePrice(items);

Does anyone know what I might be doing wrong?


Solution

  • I see three main things wrong here:

    1. Bluebirds .map() needs a callback that returns a promise.
    2. Your .then() handle that is doing console.log(items) must return items so that the result promise still has items as the resolved value.
    3. When you call returnInvoicePrice() you have to use .then() to retrieve it's value. That function returns a promise that is a master promise that watches all the other promises in .map().

    The function that you pass to Bluebird.map() needs to return a promise that is resolved when that async operation is finished. If it returns nothing or returns a plain value, then Bluebird cannot wait until the async operation is complete and thus will return sooner than you want.

    If your version does not yet support promises, there are packages out there such as mongodb-promise that will provide you a promise interface to mongodb or you can use Bluebird's .promisify() or .promisifyAll() to promisify interfaces in mongodb.

    The latest version of mongodb for node supports promises in all its async interfaces so you can directly return the promises it offers.

    It appears from this doc for the latest mongodb that xxx.find().toArray() returns a promise so you can return that promise directly from the .map() callback like this (and also fix the other things wrong):

    function returnInvoicePrice(items) {
        return Bluebird.map(items, function(item) {
            // return promise here
            return db.collection('products').find({
                product_code    : item.product_code
            }).toArray(function(err, product) {
                var invoice_price = product[0].invoice_price;
                console.log('--product--');
                console.log(product);
                return product;
            });
        }).then(function(items) {
            console.log('--items w/ invoice price--'); // this prints BEFORE my console.log(product) for some reason...
            console.log(items);
            // also have to return items here
            return items;
        }).catch(function(error) {
           console.log('--bluebird error in returning price for each item--');
           console.log(error);
        });
    }
    
    // have to use `.then()` on the returned promise here
    returnInvoicePrice(items).then(function(items) {
        // can access items here
    });