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?

kanoko
  • 85
  • 1
  • 2
  • 8
  • 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!! – Vivek Chandra Feb 07 '12 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.. – Vivek Chandra Feb 07 '12 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. – kanoko Feb 07 '12 at 22:00
  • @Vivek: integers are no reference types in JS. Plus, he is not creating a closure. – Niklas B. Feb 07 '12 at 22:28

3 Answers3

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));
}
Community
  • 1
  • 1
Niklas B.
  • 92,950
  • 18
  • 194
  • 224
  • Unfortunately I cannot change or use the parameters for the callback_function that is being passed as a parameter. – kanoko Feb 07 '12 at 19:07
  • @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. – Niklas B. Feb 07 '12 at 19:12
  • 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. – kanoko Feb 07 '12 at 19:29
  • @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. – Niklas B. Feb 07 '12 at 20:20
  • My apologies, both of those solutions do work. Could you please explain the following syntax from solution 2: (function(i) { //code })(i) – kanoko Feb 07 '12 at 21:53
  • I will go with this solution due to browser compatibility. Thanks! – kanoko Feb 08 '12 at 20:27
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|.

}
Alexander Pavlov
  • 31,598
  • 5
  • 67
  • 93
  • I like this as well. Is .bind supported on non-Mozilla browsers? – kanoko Feb 07 '12 at 22:38
  • 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. – Alexander Pavlov Feb 08 '12 at 06:45
  • 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. – kanoko Feb 08 '12 at 20:22
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..

Community
  • 1
  • 1
Vivek Chandra
  • 4,240
  • 9
  • 33
  • 38
  • Could you explain what is happening with x here: function(value) { return value; } (x); – kanoko Feb 07 '12 at 22:27
  • `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. – Alexander Pavlov Feb 08 '12 at 13:27
  • 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? – kanoko Feb 08 '12 at 18:39
  • You might want to check the [ECMA-262 specification](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) 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. – Alexander Pavlov Feb 09 '12 at 07:00