Reputation: 4250
I'm writing a function in Parse's Cloud Code which populates the server with 5 playing cards. The idea being that the server should wait until all 5 cards are in the database before moving on to the next part of the function (which currently just outputs a message to the console.
What I'm seeing is that all 5 cards are being added to the database. They also do not necessarily appear in the database in sequence, which makes me believe they are correctly being added asynchronously. And for the mostpart my logging shows intended behaviour.
I2015-01-03T17:08:01.244Z] Attempting to create cards x5
I2015-01-03T17:08:01.245Z] Attempting to save card0
I2015-01-03T17:08:01.247Z] Attempting to save card1
I2015-01-03T17:08:01.248Z] Attempting to save card2
I2015-01-03T17:08:01.249Z] Attempting to save card3
I2015-01-03T17:08:01.250Z] Attempting to save card4
I2015-01-03T17:08:01.352Z] Card saved:4
I2015-01-03T17:08:01.353Z] Card saved:4
I2015-01-03T17:08:01.354Z] Card saved:4
I2015-01-03T17:08:01.355Z] Card saved:4
I2015-01-03T17:08:01.356Z] Card saved:4
I2015-01-03T17:08:01.357Z] ALL 5 Promises theoretically fulfilled
However, notice that when the cards are actually saved, the log for each one of them is using the same number - in this case "Card saved:4".
Question is... are my promises working as intended? and how do I fix my bug to show the actual card number that was saved?
Here's my code:
Parse.Cloud.define("populateServer", function(request, response)
{
console.log("Attempting to create cards x5");
var promises = createCards(5);
Parse.Promise.when(promises).then(function(result)
{
console.log("ALL 5 Promises theoretically fulfilled");
});
});
function createCards(qty)
{
var promises = [];
for (i=0;i<qty;i++)
{
var Card = Parse.Object.extend("Card");
var card = new Card();
card.set("name", "test");
card.set("number", i);
console.log("Attempting to save card" +i);
var promise = card.save();
promises.push(promise);
promise.then(function() {
console.log("Card saved:" +i);
}, function(error) {
console.log("Uh oh, something went wrong.");
});
}
return promises;
}
Upvotes: 0
Views: 65
Reputation: 5958
As Nick said, your counter (i
) is incremented up to 4 before any of the then
handlers execute, so naturally all handlers use the final value of i
(which is 4).
The quickest workaround around this issue is probably to enclose the assignation of the then
handler in a closure, in which you can use a variable to remember the value of i
at the time the closure was called.
For example, replace this:
promise.then(function() {
console.log("Card saved:" +i);
}, function(error) {
console.log("Uh oh, something went wrong.");
});
With this:
(function(){
var myIndex = i;
promise.then(function() {
console.log("Card saved:" + myIndex);
}, function(error) {
console.log("Uh oh, something went wrong.");
});
})();
I've made a couple fiddles to see the difference in action: Without closure - With closure
Upvotes: 2
Reputation: 29211
You actually are getting the behavior you desire, but i
is always 4 at the end of your promises because it is incremented before any of the then
handlers execute (your for loop is synchronous but your handlers are asynchronous).
Normally promises passed to all
are not resolved synchronously, but since you are creating a chain by then
ing of each promise your calls will only fire after the preceeding one has completed.
On a side note: I am not super familiar with Parse's promise implementation, but you can probably save yourself creating an extra array and calling Parse.Promise.when
by just returning the promise chain from your function:
function createCards(qty) {
// create a resolved promise that starts the "chain"
// for Parse prommises use Parse.Promise.as();
var promiseChain = Parse.Promise.as();
// start the for loop
for (i=0;i<qty;i++) {
// add another promise onto our chain
// you can `then` or `catch` off of this
// now if you want
promiseChain = promiseChain.then(function () {
// ... any additional logic
card.save();
});
}
return promiseChain;
}
// usage
createCards(5)
.then(function () {
console.log('finished');
});
Upvotes: 2