Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way to pass a variable argument in a callback in JavaScript?

I feel this should be answered somewhere in the internet but I failed to find it, maybe because I'm not searching the correct terms but this is the problem: I have the following function:

function ParentFunction (DataBase, Parameters) {            
  for (k = 0; k < DataBase.length; k++){
    var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid;
    $.ajax({
      url: CalendarURL,
      dataType: 'json',
      timeout: 3000,
      success: function( data ) { succesFunction(data, k, Parameters);},
      error: function( data ) { errorFunction ("Error",Parameters); }
    });
  }
}

I was getting errors in succesFunction(data, k, Parameters) because 'k' was always evaluated with the latest value. What is happening is that, when the for loop runs k is correctly increased but, when the callback function successFunction was executed, typically several ms after the loop was finished, it was always been evaluated with the last value of k, not the value of the loop the $.ajax was called.

I fixed this by creating another function that contains the ajax call. It looks like this:

function ParentFunction (DataBase, Parameters) {        
  for (k = 0; k < DataBase.length; k++){
    var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid;
    AjaxCall(CalendarURL, k, Parameters);
  }
}

function AjaxCall(URL, GroupIndex, Parameters) {
    $.ajax({
      url: URL,
      dataType: 'json',
      timeout: 3000,
      success: function( data ) { succesFunction(data, GroupIndex, Parameters);},
      error: function( data ) { errorFunction ("Error",Parameters); }
    });
}

and it works. I think when the function is called in the parentFunction a copy of the value of the arguments is created and when the callback executes sees this value instead of the variable k which by the time would have a wrong value.

So my question is, is this the way to implement this behaviour? Or is there more appropriate way to do it? I worry that either, different browsers will act differently and make my solution work in some situations and not work in others.

like image 772
Thelemitian Avatar asked Mar 09 '14 19:03

Thelemitian


People also ask

How do you pass arguments in callback?

Passing a function to another function or passing a function inside another function is known as a Callback Function. Syntax: function geekOne(z) { alert(z); } function geekTwo(a, callback) { callback(a); } prevfn(2, newfn); Above is an example of a callback variable in JavaScript function.

Can a callback function have arguments?

Callback FunctionsA callback function is a function that is passed as an argument to another function, to be “called back” at a later time. A function that accepts other functions as arguments is called a higher-order function, which contains the logic for when the callback function gets executed.

How do you pass parameters in callback function in react?

Passing the event object of react as the second argument. If you want to pass a parameter to the click event handler you need to make use of the arrow function or bind the function. If you pass the argument directly the onClick function would be called automatically even before pressing the button.

Which of the following argument is used to invoke callback function?

A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action. The above example is a synchronous callback, as it is executed immediately.


1 Answers

You are hitting a common problem with javascript: var variables are function-scoped, not block-scoped. I'm going to use a simpler example, that reproduces the same problem:

for(var i = 0; i < 5; i++) {
  setTimeout(function() { alert(i) }, 100 * i);
}

Intuitively, you would get alerts of 0 through 4, but in reality you get 5 of 5, because the i variable is shared by the whole function, instead of just the for block.

A possible solution is to make the for block a function instead:

for(var i = 0; i < 5; i++) {
  (function(local_i) {
    setTimeout(function() { alert(local_i); }, 100 * i);
  })(i);
}

Not the prettiest or easier to read, though. Other solution is to create a separate function entirely:

for(var i = 0; i < 5; i++) {
  scheduleAlert(i);
}

function scheduleAlert(i) {
  setTimeout(function() { alert(i); }, 100 * i);
}

In the (hopefully near) future, when browsers start supporting ES6, we're going to be able to use let instead of var, which has the block-scoped semantics and won't lead to this kind of confusion.

like image 76
Renato Zannon Avatar answered Oct 12 '22 22:10

Renato Zannon