1

I am having an issue with some code which I would expect to work. I have a variable defined outside a function and as such would expect that to be available to the function through inheritance. I console log the variable outside the function and get a value and console log inside the function and get undefined. I have used comments in the code to show these console logs. Any help here would be great. Please see code snippet below. Thanks Ant

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

    var destination = parseResult[i].attributes.userInfo;

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

        if (firebaseResult[i].$id == parseResult[i].attributes.facebookID) {


            parseResult[i].attributes.convoID = firebaseResult[i].convoID;

            console.log(firebaseResult[i].time); // this returns the timestamp value

            parseResult[i].attributes.lastMessage = FirebaseAPI.getLastMessage(firebaseResult[i]).$loaded()
                .then(function(lastMessage) {

                    console.log(firebaseResult[i].time); // this returns undefined

                    if (!(0 in lastMessage)) {

                        var returnValue = 'Matched on ' + firebaseResult[i].time;
                    } else if (0 in lastMessage) {

                        var returnValue = lastMessage[0].message;
                    }

                    return returnValue;

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

        }
    }
}
Kato
  • 40,352
  • 6
  • 119
  • 149
Ant
  • 356
  • 2
  • 15
  • 6
    You're using `i` for both of the `for` loops, that is causing `conflict`. Use `i` and `j` for loops – Tushar Jun 30 '15 at 08:48
  • 3
    @Tushar: that, and (even if he used different counters) both of them will likely be pointing to out-of-range of their respective arrays when a callback finally arrives. – Sergio Tulentsev Jun 30 '15 at 08:51
  • 2
    duplicate of [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – JJJ Jun 30 '15 at 08:52
  • @SergioTulentsev Yes, for `asynchronous` operations inside `for`, `anonymous` function should be used inside `for` to preserve correct value of `i` or `j` – Tushar Jun 30 '15 at 08:52
  • @Tushar: Better yet, a *named* function describing its purpose and being reused. – T.J. Crowder Jun 30 '15 at 09:28
  • No Firebase, Angular, or AngularFire code here, just a superfluous variable named firebaseResult. Tag appropriately. – Kato Jul 01 '15 at 19:16

2 Answers2

2

It is often not reliable to use loop iterator to access things in async callback, because when the callback come back, i would have been increased to the value that let it exit the loop.

The fix is assigning it to a variable for anything you want to hold.

console.log(firebaseResult[i].time); // this returns the timestamp value
var time = firebaseResult[i].time;

parseResult[i].attributes.lastMessage = FirebaseAPI.getLastMessage(firebaseResult[i]).$loaded()
   .then(function(lastMessage) {

       console.log(firebaseResult[i].time); // this returns undefined
       console.log(time);
Icycool
  • 7,099
  • 1
  • 25
  • 33
1

It's because you're in the callback of your FirebaseAPI.getLastMessage() call. If you log this (your scope) in it, you'll get something like a FirebaseAPI object or something.

What you can do is the classic var self = this; trick to keep your context stored in a variable accessible through scopes.

This would look like:

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

var destination = parseResult[i].attributes.userInfo;

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

    if (firebaseResult[i].$id == parseResult[i].attributes.facebookID) {


        parseResult[i].attributes.convoID = firebaseResult[i].convoID;

        console.log(firebaseResult[i].time); // this returns the timestamp value
        // Store your context here
        var self = this;
        this.results = firebaseResult[i].time;

        parseResult[i].attributes.lastMessage = FirebaseAPI.getLastMessage(firebaseResult[i]).$loaded()
            .then(function(lastMessage) {

                console.log(firebaseResult[i].time); // this returns undefined
                // Get your values
                console.log(this.results); // this returns your values.

                if (!(0 in lastMessage)) {

                    var returnValue = 'Matched on ' + firebaseResult[i].time;
                } else if (0 in lastMessage) {

                    var returnValue = lastMessage[0].message;
                }

                return returnValue;

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

    }
}

}

I see in your tags that you're using Angularjs, so you could use the $scope object to store your scope variable and easily deal with promises and such.

Hope this helps :)

hugohil
  • 43
  • 10
  • Thanks very much that's great, I accepted the first answer that worked. This answer explains how to use this and was very informative. Thank you for taking the time :) – Ant Jun 30 '15 at 09:06
  • No problem :) First answer was indeed also taking in consideration the fact that you are in a loop. – hugohil Jun 30 '15 at 09:14