2

I have the following code with Angular.js:

$scope.createSSH = function(){
             for (var j=0; j<allGroupsArrays.length; j++){
                for (var i=0; i<$scope.appArray.length; i++){
                  allGroupsArrays[j][i][6] = Boolean(0);
                  allGroupsArrays[j][i][7] = Boolean(0);
                  // Some irrelvant code
                  WClient.createSSHK(allGroupsArrays[j][i][2], allGroupsArrays[j][i][3], allGroupsArrays[j][i][4], i, allGroupsArrays[j], allGroupsArrays[j][i][7], j).then(
              function(data) {
                    console.log("The j is: " + j)
                    console.log("The i is: " + i)
                  },
              function(message) {
                    console.log("Error")
                  }

When the callback is backed, I notice that j and i variables value were note save. Instead, It prints the following:

The j is: 5
The i is: 3

These even when the callback was executed were i was 0 and j was 0.

How can I save the variable's values context?

Or Smith
  • 3,556
  • 13
  • 42
  • 69
  • That is correct. You'll always get those values. That's because while you execute the loop the callback wont be called until the createSSHK is done. Remember that is asynchronous. You may need to pass those variable to create SSHK and then accept it in callback. – Akshay Khandelwal Dec 10 '14 at 08:11

3 Answers3

0

When callback function called, i and j values are taken from parent context, and as they are incremented, values will always be last incremented (5 and 3 respectively). To overcome this, you must pass current values to be used:

.then((function(i, j) {
    return function(data) {
        console.log("The j is: " + j)
        console.log("The i is: " + i)
    };
})(i, j))

Here you create function receiving current values (function(i, j) {...}, invoke it immediately (i, j)) and having them independent from parent context. This function returns function to be executed (return function(data) {...}). Now you have data passed by callback invoker and i j have values passed on createSSHK call.

Zudwa
  • 1,360
  • 10
  • 13
0

It's never a good idea to create functions in a loop. Try something like this:

$scope.createSSH = function(){
         for (var j=0; j<allGroupsArrays.length; j++){
            for (var i=0; i<$scope.appArray.length; i++){
              allGroupsArrays[j][i][6] = Boolean(0);
              allGroupsArrays[j][i][7] = Boolean(0);
              process(i ,j);
            }
         }

 function process(i, j){
              WClient.createSSHK(allGroupsArrays[j][i][2], allGroupsArrays[j][i][3], allGroupsArrays[j][i][4], i, allGroupsArrays[j], allGroupsArrays[j][i][7], j).then(
          function(data) {
                console.log("The j is: " + j)
                console.log("The i is: " + i)
              },
          function(message) {
                console.log("Error")
              }
 }
Joel Jeske
  • 1,618
  • 14
  • 17
0

This is closure typical problem. Loops execute and after they are complete values i and j are 5 and 3 of course. However then callback functions still use these i and j values from the outer scope. What you need to do is to create a separate scope values.

One way to do it (and there are at least 6 ways!) is using Function.prototype.bind method of the function. It works because it creates new function with scoped values for i and j:

WClient.createSSHK(allGroupsArrays[j][i][2], allGroupsArrays[j][i][3], allGroupsArrays[j][i][4], i, allGroupsArrays[j], allGroupsArrays[j][i][7], j).then(
    function(i, j, data) {
        console.log("The j is: " + j)
        console.log("The i is: " + i)
    }.bind(null, i, j),

    function(message) {
        console.log("Error")
    }.bind(null, i, j)
);

Also take a look at this very popular question on the same subject.

Community
  • 1
  • 1
dfsq
  • 191,768
  • 25
  • 236
  • 258