Emmanuel Campos
Emmanuel Campos

Reputation: 726

Flow control with Promise

I have this piece of code:

var totalAmount = 0;

Returns.find().populate("products")
    .then(function (returns){
        return returns;
    })

    .each(function (returns){
        ReturnsService.getTotalAmount(returns.products, function(err, result){
            totalAmount = totalAmount + result;
        });
    })

    .then(function (){
        return res.send({total : totalAmount});
    });

Why is the result of this code is 0, it's like the each is not being finished before the last then is being fired?

Upvotes: 3

Views: 160

Answers (1)

Jonathan Lonowski
Jonathan Lonowski

Reputation: 123453

If ReturnsService.getTotalAmount() is asynchronous, Bluebird's .each() won't wait for that to complete its work and for totalAmount to be modified without some guidance:

If the iterator function returns a promise or a thenable, then the result of the promise is awaited, before continuing with next iteration.

If getTotalAmount() provides a promise itself, a return is all that needs to be added:

.each(function (returns){
    return ReturnsService.getTotalAmount(returns.products, ...);
})

Otherwise, a new Promise() should be created for it:

.each(function (returns) {
    return new Promise(function (resolve, reject) {
        ReturnsService.getTotalAmount(returns.products, function(err, result){
            if (err)
                return reject(err);

            totalAmount = totalAmount + result;
            resolve(totalAmount);
        });
    });
})

As as aside, the scenario of iterating to determine a single value (sum total, etc.) is the intent of another method -- .reduce().

.reduce(function (runningTotal, returns) {
    return new Promise(function (resolve, reject) {
        ReturnsService.getTotalAmount(returns.products, function(err, result){
            if (err)
                return reject(err);

            resolve(runningTotal + result); // resolve with new total
        });
    });
}, 0 /* initial value */)

// combined total will be passed as the argument
.then(function (totalAmount) {
    return res.send({ total : totalAmount });
});

Upvotes: 4

Related Questions