1

In the code below I'm adding a callback function essentially to a list in a loop.

every Item in the list that I call the call back it will log >>item30

can you please tell me why? Is there a way create a new function() as the call back so that it can log

item0
item1
item2
item3
item4
and so on .......

......

  for (var j = 0 ; j < 30 ; j += 1) {

      addThisThingtoList(function () {
          console.log( "item" +j );
      });

  }
1

3 Answers 3

2

This only happens if your function addThisThingtoList() is using asynchronous behavior (like Ajax calls) and thus its callback is called some time later after the for loop has completely run its course and thus it's index value is at the ending value.

You can fix that with a closure that will freeze the loop value separately for each call to addThisThingtoList() like this:

  for (var j = 0 ; j < 30 ; j += 1) {
      (function(index) {
          addThisThingtoList(function () {
              console.log( "item" + index);
          });
      })(j);
  }

Working demo: http://jsfiddle.net/jfriend00/A5cJG/

By way of explanation, this is an IIFE (immediately invoked function expression). The variable j is passed to the IIFE and it becomes a named argument to the function that I named index. Then, inside that function, you can refer to index as the value of j that is frozen uniquely and separately for each call to addThisThingtoList(). I could have named the argument index to also be j in which case it would have just overriden the higher scoped j, but I prefer to use a separate variable name to be more clear about what is what.

Here's a good reference on the IIFE concept if you want to read more about it:

http://benalman.com/news/2010/11/immediately-invoked-function-expression/

Sign up to request clarification or add additional context in comments.

9 Comments

@Smeegs - I'm not sure what your jsFiddle was trying to illustrate. Here's a jsFiddle that illustrates the concept by calling the callback sometime later: jsfiddle.net/jfriend00/A5cJG
@jsfriend00 My fiddle illustrates the same thing, jsfiddle runs all the js at load. The setTimeOut I added at the end only executes after the load process is complete. ie. the iteration is complete and all the callback functions have been added to the list. Your example uses an arbitrary sometime later, while mine is called as soon as the load process is complete. Both essentially doing the same thing, but you chose an arbitrary length of time to wait before calling it.
@Smeegs - OK, I was confused by your implementation. You were saving an array of function pointers and then calling one of them later. I was calling them all later. I'm curious why you're using setTimeout() with no time value passed to it?
setTimeout() waits for whatever process it's created in to finish before executing. Here's a quick example: jsfiddle.net/J9wZ6 You can see that I write to the log in two 7 item increments. The first one straight up, the second with setTimeout() wrapped around the fourth call. As you can see, it get's printed last. Because it waiting for the load process to complete before writing it's part to the console.
So, in my original fiddle, I use setTimeout() to wait for the array to be completely built and the iteration to complete before executing the callback. The use of the setTimeout() ensures that all tasks associated with the process have completed.
|
1

This is what closure is all about.

When you call the function, it pulls the value of j at the time it's called, not at the time when it was added to the list.

So by the time you start calling the functions, j is already at 30.

1 Comment

thanks for the reply . any way around this in my example, thanks?
0

It's a classic javascript scope bug. You need to pass in the j variable to your function:

addThisThingToList(function(j) {
    console.log("item" + j);
});

As @Smeegs says, "this is what closure is all about."

1 Comment

You're in the right direction, but this particular implementation won't solve it. The addThingtoList() function doesn't know anything about j so it can't pass it to the callback.

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.