3

I am using a function with the following parameterization (which cannot be changed):

my_function(data, callback_function(results, status) {});

I need to pass additional information to callback_function that cannot be added to 'data' (which callback_function uses) or 'results' or 'status'. Specifically this information is the counter of a for loop that the my_function call is in.

To do this I am including a reference to the counter inside the body of callback_function:

for(var i = 0; i < 10; i++) {

  var data = 'cannot modify this data';

  my_function(data, function (results, status) { alert(i); });

}

Unfortunately the final value of i (9 in this case) is being printed 10 times. The intended behavior is for each value of i in the loop (0 through 9) to be printed.

Is it possible for dynamic functions to access variables outside of their scope but within the scope that they are defined?

4
  • your already using closures,i think.. and the value 9 is a reference to i -- which is changed to 9 at the end of the loop and hence prints 9 -- always!! Commented Feb 7, 2012 at 19:38
  • if your trying to define the myfunction with the values provided by i and later trying to call it -- its a closure..!!.. its accessing the reference to i.. Commented Feb 7, 2012 at 19:39
  • @VivekChandra The second part of my code is the actual call to my_function, not the definition. And I don't believe my_function is a closure since i is not being passed as a parameter. Commented Feb 7, 2012 at 22:00
  • @Vivek: integers are no reference types in JS. Plus, he is not creating a closure. Commented Feb 7, 2012 at 22:28

3 Answers 3

6

You need to create a closure that contains the value of i at the time where the anonymous function is created. You can use a wrapper function for that:

function createClosure(x, func) {
  return function(results, status) { func(x, results, status); }
}

/* ... */

for(var i = 0; i < 10; i++) {
  var data = 'cannot modify this data';
  my_function(data, createClosure(i, function(i, results, status) { 
    alert(i);
    alert(results);
    alert(status);
  }));
}

Or if you want to be short, you can create the closure in place:

for(var i = 0; i < 10; i++) {
  var data = 'cannot modify this data';
  my_function(data, (function(i) {
    return function (results, status) { 
      alert(i); 
    }
  })(i));
}
Sign up to request clarification or add additional context in comments.

6 Comments

Unfortunately I cannot change or use the parameters for the callback_function that is being passed as a parameter.
@user1193461: I don't understand? Maybe you want to define the callback inside the loop, in that case you need an additional layer of indirection... I updated the code, maybe it's more helpful now.
Sorry, I cannot modify the parameters of my_function or the parameters of callback_function. Your callback 'createClosure' is no longer using the required 'results' and 'status' parameters.
@user1193461: Have you actually tried it? createClosure is NOT the callback function! It merely creates a callback function for you and returns it. I added another sample which does the same, but in a more convulted way. Maybe you like it better.
My apologies, both of those solutions do work. Could you please explain the following syntax from solution 2: (function(i) { //code })(i)
|
1

What you need is Function.prototype.bind. Using that you can bind a function to certain parameters. With that in place, your code will look like this:

for(var i = 0; i < 10; i++) {

  var data = 'cannot modify this data';

  function callback(iValue, results, status) { alert(iValue); }
  my_function(data, callback.bind(null, i)); // |null| since you don't seem to need any specific |this|.

}

3 Comments

I like this as well. Is .bind supported on non-Mozilla browsers?
It is supported on Chrome 18 (perhaps eariler. It is V8-based, so not sure about Safari/JSC) as well. At the very least, the link to MDN I gave above contains an implementation for browsers that do not support this method.
It appears .bind does not work on Safari for iPhone. At least this example doesn't. And I would like to avoid adding fallback code if possible. I hope Apple resolves this in the future.
0
for(var i = 0; i < 10; i++) {

var data = 'cannot modify this data';

my_function(data, function (results, status) { 
 alert( 
  function(value){ 
    return value;
    }(i);
 )
}   //function (results, status)  ends here
);  // myfunction ends here..

}

Not tested it,but .. try it.. hope it works..

Check this link -> Javascript closures - variable scope question for a better understanding of what you are doing..

4 Comments

Could you explain what is happening with x here: function(value) { return value; } (x);
function(value) {...} is an anonymous function expression. Treat it as an ordinary function but without a name. You can assign it (e.g. window.onerror = function(e) { /* handle e */ } which will be invoked internally as window.onerror()) or you can invoke it directly, like in the case above. What happens is the value parameter receives the value of x. That is it.
I see, so (x) is treated as an argument for 'function' in much the same way that command-line arguments are handled. Is there a name for this way of passing values into functions?
You might want to check the ECMA-262 specification but I don't see much difference between fn(x) and function(param) {...}(x), the latter case just invokes the function directly rather than by reference.

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.