0

I'm kicking off a nested promise mapping and seeing the outer .then() block print a null result before the resolve in the function is called.

I feel like I must be messing up the syntax somehow. I've made this stripped down example:

const Promise = require('bluebird');
const topArray = [{outerVal1: 1,innerArray: [{innerVal1: 1,innerVal2: 2}, {innerVal1: 3,innerVal2: 4}]},{outerVal2: 2,innerArray: [{innerVal1: 5, innerVal2: 6 },  {innerVal1: 7,innerVal2: 8 }]}] ;

promiseWithoutDelay = function (innerObject) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log("promiseWithDelay" ,innerObject);
            let returnVal = {}
            returnVal.innerVal1 = innerObject.innerVal1;
            returnVal.innerVal2 = innerObject.innerVal2;
            returnVal.delay = false;

            return resolve(returnVal);
        }, 0);
    })
}
promiseWithDelay = function (innerObject) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log("promiseWithDelay" ,innerObject);
            let returnVal = {}

            returnVal.innerVal1 = innerObject.innerVal1;
            returnVal.innerVal2 = innerObject.innerVal2;
            returnVal.delay = true;
            return resolve(returnVal);
        }, 3000);
    })
}

test1 = function () {
    let newArray = [];
    let newArrayIndex = 0;

    Promise.map(topArray, function (outerObject) {

        Promise.map(outerObject.innerArray, function (innerObject) {

            Promise.all([
                promiseWithoutDelay(innerObject),
                promiseWithDelay(innerObject)
            ])
                .then(function (promiseResults) {
                    newArray[newArrayIndex++] = {result1: promiseResults[1], result2: promiseResults[2]}

                })
        })
    })
        .then(function () {
            return newArray;
        })
}

var result = test1();

console.log("got result ",result);

What I'm trying to do is loop over an outer array that has some values that I need.
These values include a nested inner array that I must also loop over to get some values.
In the inner loop I pass the outer and inner values to promise functions in a Promise.all.
When the promise functions resolve they get assigned to a return object.
It seems to be working fine except for one of the promise functions sometimes has a delay as it's doing some calculations.
When this happens it is left out of the return value because it hasn't resolved yet.

Shouldn't it wait until the inner loop with Promise.all resolves before it returns from the outer loop?

Can you point me in the right direction?

EDIT: Ended up with this solution based on @Thomas's suggestion:

test1 = function(){
    return Promise.map(topArray, function(outerObject){
        let oVal = outerObject.outerVal;
        return Promise.map(outerObject.innerArray, function(innerObject){
            innerObject.oVal = oVal;
            return Promise.all([ promiseWithDelay(innerObject), promiseWithoutDelay(innerObject)])
                .then(function(results) {
                    return { result1: results[0], result2: results[1], delay: results[2] } ;
                })
        })
    }).reduce(function(newArray, arr){
        return newArray.concat(arr);
    }, []);
}
GForce
  • 21
  • 8
  • 1
    Don't use `new Promise()` to compose promises. Instead, return `Promise.all()` calls directly. – SLaks Apr 14 '17 at 21:40
  • `Shouldn't it wait until the inner loop with Promise.all resolves before it returns from the outer loop?` no, because you don't return this promise, so the value the outer loop evaluates/waits for is an implicitely returned `undefined` value. And since this ain't a promise, there's no need to wait. – Thomas Apr 14 '17 at 22:22
  • @Thomas It doesn't return when I do console.log("returning inner newArray: ", newArray ); return resolve(newArray); ? – GForce Apr 14 '17 at 22:32
  • @SLaks Thanks for pointing that out – GForce Apr 14 '17 at 22:33
  • @GForce, sry didn't see that. But this makes it even worse. You resolve your outer Promise as soon as the first iteration has finished. It's basically the async version of `for(var value of array){ /*...*/ return something }` and you wondering that the loop doesn't finish. Check out my answer – Thomas Apr 14 '17 at 22:42
  • Avoid the [`Promise` constructor antipattern](http://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Apr 14 '17 at 22:58
  • @Bergi Thanks! I took out the constructor and I think it looks better now. Unfortunately it's still returning from the function before the value is set – GForce Apr 15 '17 at 16:58
  • @Thomas OK, I fixed that. It's only returning from the last .then(), still has undefined in the calling function though – GForce Apr 15 '17 at 16:59
  • @GForce Yeah, now the problem was that you didn't `return` the promises from the callbacks, so `Promise.map` did not know what to wait for. – Bergi Apr 16 '17 at 07:19

1 Answers1

0

I'm not entirely sure I get your problem from your stripped down example, but I think what you want to do here is this:

test1 = function(){
    return Promise.map(topArray, function(outerObject){
        return Promise.all(outerObject.innerArray)
    }).reduce(function(newArray, arr){
        return newArray.concat(arr);
    }, []);
}
Thomas
  • 11,958
  • 1
  • 14
  • 23
  • Wow, that's really clean. Thank you – GForce Apr 15 '17 at 17:02
  • Though, this just passes the array in to reduce and concats them together. I need to call the promiseWithDelay and promiseWithoutDelay functions, passing in some of the values. Messing with it now :) – GForce Apr 15 '17 at 17:12
  • Edited my question to include what I ended up with. Looks much better than the mess I started with :) – GForce Apr 15 '17 at 18:08