0

I have this code in angularjs that sends an http request to an API for each index of the loop, the problem is that I want to access the index inside the promise, but this gives me the wrong index.

for (var index = 0; index < array.length; index++) {
          console.log("index outside promise: "+index);
          var elements = array[index];      

          MyResource.save(elements).$promise.then(function(response){
              console.log("index inside promise: "+index);
              //In this part show me the error, because the index is wrong
              array[index].status = true;
           }, function failed(response){
              console.log(response);
           });
}

CONSOLE:

index outside promise: 0

index outside promise: 1

index inside promise: 2

index inside promise: 2

I WANT THIS:

index outside promise: 0

index inside promise: 0

index outside promise: 1

index inside promise: 1

2 Answers2

2

Use a let statement instead of a var statement to declare the index:

//for (var index = 0; index < array.length; index++) {
for (let index = 0; index < array.length; index++) {
      console.log("index outside promise: "+index);
      var elements = array[index];      

      MyResource.save(elements).$promise.then(function(response){
          console.log("index inside promise: "+index);
          //In this part show me the error, because the index is wrong
          array[index].status = true;
       }, function failed(response){
          console.log(response);
       });
}

Alternately use an IIFE:

for (var index = 0; index < array.length; index++) {
  //IIFE
  (function (index) {
      console.log("index outside promise: "+index);
      var elements = array[index];      

      MyResource.save(elements).$promise.then(function(response){
          console.log("index inside promise: "+index);
          //In this part show me the error, because the index is wrong
          array[index].status = true;
       }, function failed(response){
          console.log(response);
       });
  }(index));
}

Because the index variable is declared as a var, the JavaScript compiler creates a closure that shares a reference to the index variable. That value contained by the reference changes before the function inside the .then method gets invoked.

To preserve the value until the function inside the .then method is invoked, either declare it as a block scoped variable with a let statement or use the Immediately Invoked Function Expression (IIFE) idiom.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
0

You can try split the API calling logic into a separate function:

function originalFunction() {
    for (var index = 0; index < array.length; index++) {
        var elements = array[index];      

        save(elements, index);
    }
}

function save(elements, index) {
    MyResource.save(elements).$promise.then(function(response){
        doSomething(index);
    }, function failed(response){
        console.log(response);
    });
}

The above solution will make it so that you can access the correct index in the API success handler. However, you cannot print the strings as you specified because the API call is an asynchronous operation, while the other code in the for loop runs synchronously. That means that when you print a "outside" the promise statement, you cannot print the corresponding message until AFTER the API call has completed. However, the next "outside" statement does not wait until the previous API call completes before making the next API call. That is why the print statements will not be in the order you specified.

If you absolutely must print out the statements as you described, then you will need to block subsequent API calls from happening until the current API completes, which will make the overall process slower. If that is what you are wanting to do, the following code should work:

function originalFunction(nextInd) {
    // default the id of the next call to 0
    nextInd = nextId || 0;

    // If the index is valid for the current array, call the API method
    if (nextInd >= 0 && nextInd < array.length) {
        console.log("index outside promise: "+nextInd );
        var elements = array[nextInd];
        save(elements, nextId);
    }
}

function save(elements, index) {
    MyResource.save(elements).$promise.then(function(response){
        console.log("index inside promise: "+index);
        doSomething(index);

        // kick off the API call for the next element in the array
        originalFunction(index++);
    }, function failed(response){
        console.log(response);

        // kick off the API call for the next element in the array
        originalFunction(index++);
    });
}

// You can still call the originalFunction without parameters to kick it off
originalFunction();
GPicazo
  • 6,516
  • 3
  • 21
  • 24