I have below function to update rows if meeting some condition and at the end of for loop, response include how many rows updated.Despite more than zero rows updated, response shows zero. Looking at log, it seems reponse.success() fires before completing for loop.
why so?
Parse.Cloud.define("reset", function(request, response) {
var isSaveNeeded = false
var Query = new Parse.Query("price");
Query.equalTo('isActive', true);
Query.find({useMasterKey:true})
.then((results) => {
console.log("Found " + results.length + " price rows")
var currentDate = moment()
var noOfRowsUpdated = 0
for (let i = 0; i < results.length; ++i) {
var valid_till_date = results[i].get('valid_till_date');
if (valid_till_date == null) {
// if user had not selected valid_till_date then set to expire after system max no of days
var updatedAt = results[i].get('updatedAt');
if (currentDate.diff(updatedAt,'days') > 10) {
console.log("Permanent change row to be set inactive. Updated at - " + currentDate.diff(updatedAt)+ updatedAt)
results[i].set('isActive',false)
isSaveNeeded = true
}
} else if (currentDate.diff(valid_till_date) > 0) {
// check whether valid_till_date has passed
console.log("Found row with elapsed valid date " + results[i].id)
results[i].set("isActive",false)
isSaveNeeded = true
}
if (isSaveNeeded == true) {
console.log("Record needs to be saved for " + results[i].id)
results[i].save(null, {useMasterKey:true})
.then(function (user) {
++noOfRowsUpdated
console.log("reset : Object ID: " + results[i].id + " saved - " + noOfRowsUpdated)
})
.catch(function (error) {
console.log("reset : Error saving Object ID: " + results[i].id + error);
response.error(error);
})
} else {
console.log("Record not to be saved for " + results[i].id)
}
isSaveNeeded = false
} // end of for loop
//BELOW IS EXECUTED BEFORE FOR LOOP COMPLETES
console.log("Updated " + noOfRowsUpdated +" price rows");
response.success("Updated " + noOfRowsUpdated +" price rows")
}) // end of .then((results)
.catch(function(error) {
response.error("Failed to fetch from price" + error );
});
});
Parse.com's save
runs async, so that loop finishes before the saves happen. The solution is to reorganize the code a little bit, and wait for the saves to happen before executing the response functions.
The trick is to collect the promises returned by each save in an array and wait for the fulfillment of those promises with Promise.when()
(synonym for Promise.all())
To make it clearer, factor out the "is save needed" logic, so this cloud function can be only about handling the database...
Parse.Cloud.define("reset", function(request, response) {
var Query = new Parse.Query("price");
Query.equalTo('isActive', true);
Query.find({useMasterKey:true}).then((results) => {
console.log("Found " + results.length + " price rows");
// assuming ES6 or something like underscore
let pricesToSave = results.filter(price => priceNeedsSave(price));
// here's the important part, collect the promise from each save
// proceed only after the promises have completed
let promises = pricesToSave.map(price => price.save(null, {useMasterKey:true}));
return Parse.Promise.when(promises).then(() => pricesToSave.length);
}).then(count => {
response.success("Updated " + count +" price rows");
}).catch(error => {
response.error("Failed to fetch from price" + error );
});
}
Just for completeness, below is the factored-out needsSave logic. (OP should check this over, I just copied the body of the loop)...
function priceNeedsSave(price) {
var isSaveNeeded = false;
var currentDate = moment()
var valid_till_date = price.get('valid_till_date');
if (valid_till_date == null) {
// if user had not selected valid_till_date then set to expire after system max no of days
var updatedAt = price.get('updatedAt');
if (currentDate.diff(updatedAt,'days') > 10) {
console.log("Permanent change row to be set inactive. Updated at - " + currentDate.diff(updatedAt)+ updatedAt)
price.set('isActive',false)
isSaveNeeded = true
}
} else if (currentDate.diff(valid_till_date) > 0) {
// check whether valid_till_date has passed
console.log("Found row with elapsed valid date " + price.id)
price.set("isActive",false)
isSaveNeeded = true
}
return isSaveNeeded;
}