0

New to javascript here, so callbacks are still a little iffy in my brain.

What I'm trying to do is: given a "menu" which is an array of objectId's, query for each foodItem that corresponds to that objectId, get its votes, put it in a min-heap (to determine which are the top 5 items), and return those top 5 items.

My heap at the end is empty because I realize that JavaScript is asynchronous and that when I try to get the heap data, the callback might not have necessarily completed.

If it were just one call, I would just nest the callbacks, but since this is a loop I'm not really sure what to do.

function getTopFoods(menu, heap, callback) {
//go through each objectId, get its foodItem and then its votes, then heap it
    console.log("got to TopFoods");
        for (var i = 0; i < menu.length; i++) {
            var foodID = menu[i];
            var FoodItem = Parse.Object.extend("FoodItem");
            var foodQuery = new Parse.Query(FoodItem);
            foodQuery.equalTo("objectId", foodID);
            //get corresponding foodItem
            foodQuery.find({
                success: function(foodResult) {
                    //got specific food Item
                    var votes = foodResult.get("votes");
                    console.log("votes: " + votes);
                    if (heap.length < 5) {
                        heap.queue(foodResult);
                    } else {
                        if (votes > heap.peek().get("votes")) {
                            heap.dequeue();
                            heap.queue(foodResult);
                        }
                    }
                }, 
                error: function(error) {
                    console.log("Food error: " + error.code + " " + error.message);
                }
            }); 
        }
        var topFoods = [];
        for (var i = 0; i < 5; i++) {
            topFoods[i] = heap.dequeue();
        }
        callback(topFoods);
}

1 Answer 1

1

The easiest way is to use promises; at this stage, this involves using a library (coming to JavaScript proper in ES6). If you want a low-tech solution, just count stuff:

var waitingCount = menu.length;
for (....) {
  ...
  success: function(foodResult) {
    ...
    if (!--waitingCount) {
      callback(topFive(heap));
    }
  },
  error: function(error) {
    --waitingCount;
    ...
  }
  ...
}

This is just the basic idea. It would be good if you also decremented the counter on failed responses, since this way a single fail will leave you hanging.

EDIT: Err, obviously, the check needs to go to the bottom of success, not to the top as my snippet indicated before, or you'll miss the last element. I also put in the error case.

EDIT2: As eth3lbert notes, parse.com API also supports promises (I don't work with parse.com, so... thanks for the tip). In that case, here's what you do:

var promises = [];
for (....) {
  var promise = foodQuery.find({
    ...
  });
  promises.push(promise);
});
Parse.Promise.when(promises).then(function()) {
  callback(topFive(heap));
}
Sign up to request clarification or add additional context in comments.

3 Comments

Agree, Parse javascript sdk support promises and you should give it a try.
Sort of related question: each of these queries in the loop require that response.success() or response.error() be called (but I obviously don't want it called prematurely). Are promises the best way to handle this?
Promises are the cleanest way to do this: they are more legible than doing the bookkeeping yourself. They do not do anything hugely different under the hood, though. I put in a promise solution too...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.