-2

I'm struggling to understand how I can return data from multiple promises to build up an array of data.

Is there anyway I can return the data outside of the promise to push to the data variable?

I have the following:

db_sh.find({
    selector: {sh: req.params.sh_id},
    fields: ['_id', 'sh_id', 'time'],
    sort: ['_id']
}).then(function (result) {
    let data = {};
    console.log('Found: ' + result.docs.length);
    if (result.docs.length > 0) {
        for (var i = 0; i < result.docs.length; i++) {
            let student = result.docs[i];

            Promise
                .all([getMealBooking(student._id), getStudentData(student._id)])
                .then(function(response) {
                    var meal_booking_data = response[0];
                    var student_data = response[1];
                    console.log(meal_booking_data);

                    console.log(student_data);
                })
                .catch(function (err) {
                    return res.send(false);
                });

            data[student.time] = [
                meal_booking_data,
                student_data
            ]
        }
    }
    /** Sort Data Oldest First*/
    data = Object.keys(data).sort().reduce((a, c) => (a[c] = data[c], a), {});
    console.log(data);
    res.send(data);
});

I have two promises (getMealBooking() & getStudentData()): and I am using Promise.all() to return me the results of both of these promises. I have tried to return the data but I cannot get the results to build up the data array.

Any help to be able to build up a list of all my data would be great.

lky
  • 1,081
  • 3
  • 15
  • 31
  • 1
    Does this answer your question? [How to return the response from an asynchronous call](https://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call) – jonrsharpe May 17 '22 at 00:24
  • No way to "return the data outside of the promise" - asynchrony remains asynchronous – Bravo May 17 '22 at 00:32

3 Answers3

1

You need two Promise.alls - one to iterate over each student, and a nested one to fetch the getMealBooking and getStudentData for each student.

Put everything into an async function (that catches and sends false if needed) to make the control flow easier to understand.

const { docs } = await db_sh.find({
    selector: { sh: req.params.sh_id },
    fields: ['_id', 'sh_id', 'time'],
    sort: ['_id']
});
if (docs.length === 0) {
    // no data; stop here
    res.send({});
    return;
};
const data = {};
await Promise.all(
    docs.map(student => (
        Promise.all([getMealBooking(student._id), getStudentData(student._id)])
            .then(([mealBookingData, studentData]) => {
                data[student.time] = [mealBookingData, studentData];
            })
    ))
);
const sortedData = Object.keys(data).sort().reduce((a, c) => (a[c] = data[c], a), {});
res.send(sortedData);
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • See the OP edit, which now has those gets dependent on the student – danh May 17 '22 at 00:28
  • Apoligies, I missed passing the student ID into the getMealData() and getStudentData() calls. Thanks for you answer, only just learning how JS promises work and this has helped a lot with chaining then() and returning Promise.all(). Thanks for sharing this approach :) – lky May 17 '22 at 00:32
  • @heady12 OK, see edit – CertainPerformance May 17 '22 at 00:48
  • Thanks for your answer, You have really helped me with both your responses with me understand whats going on. Unfortunetly, this logic is within my express.js endpoint and I'm unable to use await (SyntaxError: await is only valid in async functions and the top level bodies of modules). I'm accepted danh answer as I've used his approach but will defo be referring back to this. Many Thanks :) – lky May 17 '22 at 00:52
  • @heady12 Like my answer says, you should make the function async - you just need to add the `async` keyword. (Don't be afraid of using async functions, they often make async code a lot easier to understand. There isn't a *ton* of benefit here, but it's not bad) – CertainPerformance May 17 '22 at 00:53
  • Sorry, I didn't know I could make express.js endpoints async, just seen they can lol. Thanks again. I just tested your code but for some reason it just returns 1 as data? – lky May 17 '22 at 00:58
  • @heady12 There's no way it should return 1; all calls of `res.send` return an object – CertainPerformance May 17 '22 at 01:00
  • Sorry, I made a mistake in my query, data was returning 1 when I had error from pouchdb - code work fine :) Thanks again – lky May 17 '22 at 01:05
  • Accepted This answer - code is cleaner and easier to understand - although danh answer also helpful. Thanks to both. – lky May 17 '22 at 01:08
1

Another Promise.all() is needed for the loop that contains the Promise.all() you've already figured out. It's better to factor a little so you can see what's happening.

function getStudentMealAndData(student) {
  return Promise
    .all([getMealBooking(student._id), getStudentData(student._id)])
    .then(function(response) {
      var meal_booking_data = response[0];
      var student_data = response[1];
      console.log(meal_booking_data);
      console.log(student_data);
      return { student, meal_booking_data, student_data };
    })
    .catch(function (err) {
      return res.send(false);
    });
}

This simplifies the then block a bit...

}).then(function (result) {
    console.log('Found: ' + result.docs.length);
    let promises = []
    for (var i = 0; i < result.docs.length; i++) {
      let student = result.docs[i];
      promises.push(getStudentMealAndData(student));
    }
    return Promise.all(promises);
}).then(results => {
  // results are an array of [{ student, meal_booking_data, student_data }, ...]
  let data = results.reduce((acc, s) => {
    acc[s.student.time] = [ s.meal_booking_data, s.student_data ];
    return acc;
  }, {});
  data = Object.keys(data).sort().reduce((a, c) => (a[c] = data[c], a), {});
    console.log(data);
    res.send(data);
});
danh
  • 62,181
  • 10
  • 95
  • 136
-1
let arr = [];
const datas = await Promise.all([
    getMealBooking(),
    getStudentData()
]);
arr.push(datas[0]); //adds getMealBooking() results
arr.push(datas[1]); // adds getStudentData() results
chaoskreator
  • 889
  • 1
  • 17
  • 39