NickDK NickDK - 3 months ago 16
Javascript Question

Javascript promises chained inside Loop

I'm just learning Javascript and still pretty new to Parse's cloud code. I've been reading some articles and questions on promises and closures and still don't quite understand how to accomplish what I want do do yet. All the other questions/answer seem to be slightly different or difficult to understand.

I have a function that starts with a query that gets all the "Gyms". For each of those gyms, I need to run several other queries. All those inner queries (inside the loop) need to all complete before I can generate a final report for the gym. I want to understand the following things:

a.) How to allow the correct gym object from that each iteration of the loop to be accessible through the entire chain of queries in that iteration.

b.) Will all the results from the previously executed queries in my chain be available in the following queries? e.g. Can I access newWorkouts in the last function?

function createReports() {

var gymQuery = new Parse.Query(Parse.Object.extend("Gym"));

gymQuery.find({
success: function(results) {
for (var i = 0; i < results.length; ++i) {
/* jshint loopfunc: true */
var gym = results[i];


var newWorkoutsQuery = new Parse.Query(Parse.Object.extend("Workout"));
newWorkoutsQuery.equals("gym", gym);

newWorkoutsQuery.find().then(function(newWorkouts) {

var newLogsQuery = new Parse.Query(Parse.Object.extend("WorkoutLog"));
newLogsQuery.equals("gym", gym);
return newLogsQuery.find();

}).then(function(logsLastWeek) {
//Generate final report for gym using data from all above queries.
//Need access to gym, newWorkouts, and logsLastWeek

});
}
},
error:function() {
console.log("error");
}
});
}

Answer

Promise.all() should be able to help you out with this.

First, let's break out a function that retrieves the data for a single gym:

function getGymData(gym) {
    var newWorkoutsQuery = new Parse.Query(Parse.Object.extend("Workout"));
    newWorkoutsQuery.equals("gym", gym);

    var newLogsQuery = new Parse.Query(Parse.Object.extend("WorkoutLog"));
    newLogsQuery.equals("gym", gym);

    return Promise.all([newWorkoutsQuery.find(), newLogsQuery.find()])
        .then(function (results) {
            return {
                gym: gym,
                workouts: results[0],
                logs: results[1]
            };
        });
}

Then use Promise.all() across all the gyms:

function createReports() {    
    var gymQuery = new Parse.Query(Parse.Object.extend("Gym"));

    return gymQuery.find()
        .then(function (gyms) {
            return Promise.all(gyms.map(getGymData));
        })
        .then(function (results) {
            // results should be an array of objects, each with
            // the properties gym, workouts, and logs
        })
        .catch(function (error) {
            console.error(error);
        });
}