2
votes

I am writing a javascript code using Parse. I have User class and Subscription class. All users' subscription information are stored on Subscription class so it is like one to many relationship.

I would like to retrieve user information and each user's subscription count and store it in an object array.

What I have tried is as follows:

    var user = Parse.User;
    var query = new Parse.Query(user);
    query.find().then(function(objects) {
        return objects;
    }).then(function (objects) {

        for(var i = 0; i < objects.length; i++) {

            var object = objects[i];

            var subscription = Parse.Object.extend('Subscription');
            var queryForSubscription = new Parse.Query(subscription);
            queryForSubscription.equalTo('subscriptionUser', object);
            queryForSubscription.count().then(function(subscriptions) {

                var userEmail = object.get('email');
                var userDOB = object.get('userDOB');
                var userJoinedAt = object.createdAt;
                var userGender = object.get('userGender') ? 'Male' : 'Female';
                var subscriptionCount = subscriptions;

                var sub = [
                    userEmail,
                    userDOB,
                    userGender,
                    userJoinedAt,
                    subscriptionCount
                ];
                userArray.push(sub);
            });
        }
    });

However, userArray only contains last user's information repetively. I reckon it is because wihitn the second query promise (queryForSubscription).count()) runs after the outer for loop finishes.

Let's say I got User A and User B. User A has two subscriptions whereas User B has none. What I expected is something like this:

    [{User A's email, User A's dob, User A's Gender, User A's Joined Date, 2},
    {User B's email, User B's dob, User B's Gender, User B's Joined Date, 0}]

However, the result is

    [{User A's email, User A's dob, User A's Gender, User A's Joined Date, 2},
    {User A's email, User A's dob, User A's Gender, User A's Joined Date, 0}]

I'm stuck at the moment. Have been trying to use Promise and etc but got no idea..Please help!

UPDATE

GOTTA LOVE SO!!! THANK YOU EVERY SINGLE ONE OF YOU GUYS SO MUCH!! One more question here. Actually, I got one more class called Favourite to get a count of each user's number of favourites.

If the number of User A's favourite is 5 and 3 for User B, the expected result will look like this in the end:

    [{User A's email, User A's dob, User A's Gender, User A's Joined Date, 2, 5},
    {User B's email, User B's dob, User B's Gender, User B's Joined Date, 0, 3}]

I am not too sure where I put a new query for Favourite class...Need your advice again!

2
It may be a good idea to put what parse code you have into your question. That way people can see what you have and help you complete it. - JayDev
Thanks Jay! it was my first time to ask a question on stackoverflow :) I just edited my post! - david park
What's your problem ? - merours
You should read about javascript closures., seem needed for object in the last function - Hacketo
Please also add, what is the expected output, and what is the actual result. People now can only guess whats wrong without you explaining, how your result differs from what you expect. Also if there are runtime errors or anything what helps finding the issue, feel free to list it in the question. Welcome to SO :) - Balázs Édes

2 Answers

2
votes

You can go with @Hacketo's answer.

Another hacky way to get around this bug is to use .forEach() :

}).then(function (objects) {
    objects.forEach(function(object) {
        var subscription = Parse.Object.extend('Subscription');
        ...
    });
});

A new object variable will be allocated for each iteration of the loop, and you should escape the "shared var" bug.


pointing out the bug

One of the common javascript pitfalls is : a local variable's scope is not its closest bloc, it is its closest function.

The following snippet :

function (objects) {
    for(var i = 0; i < objects.length; i++) {
        var object = objects[i];
        ...

is actually equivalent to :

function (objects) {
    var i, object;
    for(i = 0; i < objects.length; i++) {
        object = objects[i];
        ...

So the object variable in the OP's code is actually shared by all the created queryForSubscription variables, and when any of the .then(..) callbacks is executed (after the loop finishes, as the OP correctly pointed out), this object variable holds the value of the last item in the array.

2
votes

It seem to be that you need to save the value of object when the last functions are executed. Try this :

for(var i = 0; i < objects.length; i++) {

    var object = objects[i];

    queryForSubscription.count().then(
      (function(myObject){
        return function(subscriptions) {
            var userEmail = myObject.get('email');

            ...    

        };   
      })(object)
    );
}