0

I am trying to get a nested Promise.all * map logic to work. I keep getting undefined values when I reach getData2.

exports.getData = (param1, param2) => {
  return getData0(param1, param2)
    .then(data0 => Promise.all(data0.map(e => getData1(e.id))))
    .then(data1 => Promise.all(data1.map(i => getData2(i.id))))
    .then(data2 => data2.map(a => getData3(a.id)))
    .catch(err => console.error(err.message));
};

P.S: 1. getData0 to getData1 return structures (e.g. { a: val1, b: val2 }) 2. I assume the problem is in the way getData's are written. I suspect they should return promises. Can anyone give me a dummy example about a function that returns a structure wherein both elements of the (see a and b above) are obtained in an async way?

Thanks!

Hochman7G
  • 174
  • 1
  • 8
  • 1
    Are `getData1()`, `getData2()` and `getData3()` asynchronous operations? If so, they should return a promise that is resolved with their result when the async operation is done. Also curious why you used `Promise.all()` with `data0.map()` and `data1.map()`, but not with `data2.map()`. If they are not asynchronous operations, then there is no need to use `Promise.all()` with them. In fact, you can combine all sequential synchronous code into one `.then()` handler. – jfriend00 Aug 02 '17 at 16:30
  • 1
    Stackoverflow works better if you show us your actual code (not pseudo-code) and we help you fix it rather than you ask us to make up examples that might or might not teach what you actually need. So, please show us the actual code for `getData1()`, `getData2()` and `getData3()` and you will likely get very useful and specific help. – jfriend00 Aug 02 '17 at 16:53
  • I'm aware of that but sometimes it's easier to get the concepts clarified first before one can post a code snippet. Dhruv's reply explains the different concepts I wasn't sure about. Also, I've added an answer to summarise the solution. – Hochman7G Aug 03 '17 at 10:49
  • No. Stack overflow works better if you include your actual code - always. Without your actual code, we're left to guess how to best answer what actually matters to you. And, with your actual code, we have opportunities to show you better ways to do things that you will not think of yourself, may not even know to ask about. So, it's not better to "get concepts clarified first". It's best to get concepts clarified in the exact context of your actual code. I post answers with code AND with conceptual explanation. – jfriend00 Aug 03 '17 at 20:03
  • Kentoj of course – Hochman7G Aug 03 '17 at 21:28

2 Answers2

3

Firstly, all your getData* methods should return Promise objects if they are doing any asynchronous operations. (for e.g fetching data).

getData3 could be an exception to this since it doesn't look like anything needs to be done after all getData3 calls are completed. If that's not the case you could use similar method for getData3 as for above. e.g data2 => Promise.all(data2.map(a => getData3(a.id)))

Now let's look over the code line by line

exports.getData = (param1, param2) => {
  return getData0(param1, param2)
    .then(data0 => Promise.all(data0.map(e => getData1(e.id))))
    // data0 here should be an Array

    .then(data1 => Promise.all(data1.map(i => getData2(i.id))))

    // 1. data1 will be an Array with results from each getData1 call
    //    mapped by index. for e.g [result1, result2, ...]
    // 2. depending on the type of result (Object/Array) from getData1
    //    you should extract `id` from `i`(result item) and then 
    //    trigger `getData2`using that `id`. I'm assuming the issue is 
    //    here. 

    .then(data2 => data2.map(a => getData3(a.id)))
    .catch(err => console.error(err.message));
};

As for Can anyone give me a dummy example about a function that returns a structure wherein both elements of the (see a and b above) are obtained in an async way? I believe this should answer that How to use Promise.all with an object as input

Dhruv Parmar
  • 419
  • 3
  • 6
  • marked your answer as the valid one as it covers different concepts involved in the logic I described - check my new answer below – Hochman7G Aug 03 '17 at 10:47
0

I've marked Dhruv's answer as the valid one as it explains different concepts involved in the logic I described.

getData1 and getData2 indeed make calls to an async function (in my case: doSomeEWSwork below) while getData3 is sync.

function doSomeEWSwork(param) {

  var ewsFunction = '.....';
  var ewsArgs = ....;

  return ews.run(ewsFunction, ewsArgs)
    .then(result => result.something)
    .catch(err => console.log(err.message));
}

My old getData1 and getData2 used to return structure objects (e.g. {a: val1, b: val2} instead of promises (needed for Promise.all). That was causing the async blocs to never execute/evaluate.

My new getData1 and getData2 have the following skeleton:

function getData1_or_2(param) {

  var promise = new Promise(function(resolve, reject) {
    doSomeEWSwork(param) // <- important: settle this using 'then'
    .then(res => {
      var ret = { a: res.val1, b: res.val2 };
      resolve(ret);
    }).catch(err => {
      console.log(err.message);
      reject(Error(err.message));
    });
  });
  return promise;
}

So the main method (already included in my original question) works fine now since I am returning promises for async and object for sync.

exports.getData = (param1, param2) => {
  return getData0(param1, param2)
    .then(data0 => Promise.all(data0.map(e => getData1(e.id))))
    .then(data1 => Promise.all(data1.map(i => getData2(i.id))))
    .then(data2 => data2.map(a => getData3(a.id)))
    .catch(err => console.error(err.message));
};

It's just the combination of nested promises, map and structure objects that confused me. Thanks!

Hochman7G
  • 174
  • 1
  • 8
  • FYI, this type of construct `.catch(err => console.log(err.message));` will "eat" the rejection and not propagate it. When you `.catch()` a rejection and do not rethrow it, then it is considered "handled" just like in `try/catch()` and the promise is changed to resolved. So, if you intend to just log the error and still propagate the rejection, you have to `rethrow`: `.catch(err => {console.log(err.message); throw err;});` So, your code as shown is logging the rejections and turning them into resolved promises with an `undefined` resolved value. – jfriend00 Aug 03 '17 at 21:39
  • Yes, I'm familiar with the concept. That was a test code and I was more interested in the console logs. Thanks. – Hochman7G Aug 04 '17 at 13:30