views:

63

answers:

3

I'm at a loss right now. I'm using a simple variable, whose value is assigned during a loop. After exiting the loop, the value of the variable is still undefined, unless I alert it's value first. Then everything works fine. What's going on here?

$(myarray).each(function(idx, item)
{
     fetchSomethingRemotely( success: function(data) {
           item.someValue = data; });

     // if the following alert is not there, doSomething will never get called
     // and the alert after the else will show item.someValue as undefined.
     alert(item.someValue);

     if (item.someValue != null) { doSomething(item.someValue); }
     else { alert(item.someValue); }

});

Edit:

Okay, so I've got a better handle in this now. The value assignment (item.someValue=123) happens inside of a callback function within this iteration. So the value is probably not there yet when I serially try to access it a couple of code lines below. How could I wait for the value to be assigned?

+2  A: 

Did you remember to use var to define the variable. Are you sure the variable exists in the scopes you are using it. If you do for ( var i ... then it will only exist in the for scope, not outside it. You can use Webkit (Chrome, Safari)'s Developer Tools to debug your script by setting a breakpoint on the problem line, and then in the right column you can see all variables defined in the related scopes.

balupton
@balupton - JavaScript does not have block level scope. A `var` inside a `for` will have function level scope or global scope.
Anurag
Unfortunately there's nothing to set the breakpoint to. Either it works (with the alert there) or it doesn't.
Alex
Anurag good point. Guess I wrote that as spending a few hours debugging something like that before in my life. I wonder if all browsers treat this the same.
balupton
Alex, breakpoint the line before alert. If the variable is in the scope you are good. At least it's a start to figuring it out.
balupton
How could it be out of scope. I'm iterating through an array of objects and set the value of its property explicitly.
Alex
+4  A: 

How could I wait for the value to be assigned?

The answer is already in your code. Just move doSomething into the callback function.

fetchSomethingRemotely( { success: function(data) {
       item.someValue = data;
       if (item.someValue != null) doSomething(item.someValue);
} });

Note that this will still move on to the next item before the current item has got its value. If you must perform all of the iteration sequentially, you can do something like this:

function iterate(index) {
  var item = myarray[index];
  fetchSomethingRemotely( { success: function(data) {
    item.someValue = data;
    if (item.someValue != null) doSomething(item.someValue);
    if (index < myarray.length - 1) iterate(index + 1);
  } });
}

And then you would fire off the whole process with iterate(0).

casablanca
I solved it slightly differently as I didn't want the processing in the callback by adding counters (started operations and completed operations) and then comparing those values in a loop until they match, and then proceed. But this is a good solution too.
Alex
@Alex: Do you mean you're running an idle loop until the counters match? That can potentially lead to a deadlock (read: freeze your browser) if the browser runs all JavaScript in a single thread, not to mention you're wasting CPU cycles.
casablanca
+1  A: 

"How could I wait for the value to be assigned?"

Welcome to asyncronous programming!

You're going to need to put everything in the callback, not just the variable assignment.

morgancodes