0

I call a function in a for, in this way:

for(var i=0; i<$scope.staffdispensers.length; i++){     
                        console.log($scope.staffdispensers[i].dispenser.iddispenser);
                        restService.Enforsaleproductsbyiddispenser($scope.staffdispensers[i].dispenser.iddispenser, _getnumber, _error);
                }

This is the implementation of this function:

Enforsaleproductsbyiddispenser: function(id, _getnumber, _error){
        var currentToken = _GetToken();

        _timerTokenControl(currentToken, _error);

        if (setupTime == null) {
            console.log("token scaduto");
            $window.location.href="login.html";
        }


        if (currentToken !== null) {
        $http({  
                    method : 'GET',  
                    url : REST_URL+'products/nforsalebyiddisp/'+id
                }).then(function successCallback(response) {  
                    _getnumber(response);
                }, function errorCallback(response) {  
                    console.log(response.statusText);  
                });  
           }  else {
                $window.location.href="login.html";
                console.log("NON SEI LOGGATO!!!");
        }
    }

Look this. "_getnumber(response);" is called after finishing all the for loop. This is _getnumber function:

function _getnumber(response){
                number.push(response.data);  
                console.log(number);

}

Looking at the console, in the number array, I have all the results of the function for each i:

enter image description here

But if I try to refresh the page, I have the same values but in a different order!

enter image description here

And this happens because "_getnumber(response);" is called after finishing all the for loop!! Look that:

enter image description here

I want to call _getnumber step by step, i by i

In main-employee.js:

enter image description here

In rest-services.js:

enter image description here

Giacomo Brunetta
  • 1,409
  • 3
  • 18
  • 38
  • Is the problem the order of the result? if so, you may use an asyncronous for with promises. – briosheje Jan 13 '17 at 17:33
  • The problem is the order! I want these values in order, but in this case the order is random – Giacomo Brunetta Jan 13 '17 at 17:34
  • That is because the http requests are asyncronous, hence some may finish before others. There are mainly two ways to solve the issue: 1) a regular loop with indexes. 2) an asyncronous loop with promises. – briosheje Jan 13 '17 at 17:36
  • Lexical Scoping man. Google it. – jdmdevdotnet Jan 13 '17 at 17:40
  • 1
    Possible duplicate of [Calling an asynchronous function within a for loop in JavaScript](http://stackoverflow.com/questions/13343340/calling-an-asynchronous-function-within-a-for-loop-in-javascript) – Heretic Monkey Jan 13 '17 at 17:40
  • @briosheje can u show me an example of this two steps? – Giacomo Brunetta Jan 13 '17 at 17:41
  • @AlfonsoSilvestri: Yes, I was working on that on a jsfiddle, though I (obviously) can't run it, so take it as an example only: First of all, you need to return a promise from your service, in this way you will be able to track when a request is finished and perform the other one. Next, you can adapt it to an asyncronous self-invoked recursive function. This is not the most elegant way to do that, but will likely work everywhere. Example that "should" work with your code: http://pastie.org/10989851 – briosheje Jan 13 '17 at 17:43
  • Oh sorry, you need also to add an if in the asyncLoop: `if (i === max) return;` – briosheje Jan 13 '17 at 17:44
  • Another possible solution is passing `i` to the get number function and, instead of pushing to an array, setting an object key and, then, convert the object to an array. – briosheje Jan 13 '17 at 17:51
  • "Uncaught (in promise) TypeError: Cannot read property 'then' of undefined" in .then(function (done) – Giacomo Brunetta Jan 13 '17 at 17:59

3 Answers3

1

you are performing an ajaxCall with $http. This particular $http call is asynchronous and there is no guarantee how long it will take.

You are calling _getNumber when the call finished successfully, when you are bursting out several calls, the first call can complete after the last one.

so therefor you will not have a guaranteed order.

return a promise:

if (currentToken !== null) {
            return $http({  
                    method : 'GET',  
                    url : REST_URL+'products/nforsalebyiddisp/'+id
                }).then(function successCallback(response) {  
                    _getnumber(response);
                }, function errorCallback(response) {  
                    console.log(response.statusText);  
                });  
        }  else {
                $window.location.href="login.html";
                console.log("NON SEI LOGGATO!!!");

                var def = $q.deferred();
                def.resolve();
                return def.promise;
        }

write a recursive function that calls itself when the promise is resolved

    function recursiveCall(i) {
            if (i < $scope.staffdispensers.length) {
                console.log($scope.staffdispensers[i].dispenser.iddispenser);
restService.Enforsaleproductsbyiddispenser($scope.staffdispensers[i].dispenser.iddispenser, _getnumber, _error)
                .then(function(){
                    recursiveCall(i+1)
                })
            }
        }

call the initial recursive call

recursiveCall(0);
Adrian Bratu
  • 498
  • 2
  • 9
  • you need to have your function to return a promise and to execute the other call when the promise was resolved, please check the updated answer – Adrian Bratu Jan 13 '17 at 17:48
  • the idea there is, you need to syn the calls up in order to guarantee the order of your console log i used in the example before a recursive call that only calls itself on the success... – Adrian Bratu Jan 13 '17 at 17:51
  • please note that i did not bother to handle the failed cases.. this is the happy path.. but this should sync up all your calls.. lemme know if it does the trick... cheers – Adrian Bratu Jan 13 '17 at 17:52
  • This solve correctly my problem! Thank you very much – Giacomo Brunetta Jan 13 '17 at 18:06
0

You're calling an asynchronous function meaning that the for loop will likely complete before you get all the responses. The order depends on when each async call completes. If you need to get data in order, you need to implement a mechanism for ordering responses OR wait for the call to complete. One way of doing that is calling fetch again in the success callback until all server calls are made.

Max Sindwani
  • 1,267
  • 7
  • 15
  • And how can I do it? – Giacomo Brunetta Jan 13 '17 at 17:39
  • A simple way to do it without promises and waiting is to pass the index at which the response of the async call should correspond to. Initialize an array of length `$scope.staffdispensers.length` and then set `arr[i] = response.data`. The downside there is that you won't know when all of the async calls complete. – Max Sindwani Jan 13 '17 at 17:58
0

Ah yes. You see, there is no certainty that the $http calls will return in the same order. Sometimes response is slow, sometimes it's fast. The only way to enforce an order is by wise use of promises. It's not hard to do...

You must use the return value of the $http call, which is a promise, to enforce the order of results.

var promises = [];  // this will hold promises in correct order
for(var i=0; i<$scope.staffdispensers.length; i++){     
  console.log($scope.staffdispensers[i].dispenser.iddispenser);
  var promise = restService.Enforsaleproductsbyiddispenser($scope.staffdispensers[i].dispenser.iddispenser, _getnumber, _error);
  promises.push(promise);
}

Now you can wait until all promises are finished by using the $q provider:

$q.all(promises).then(function(arrayOfPromises) {  //array is of all resolved promises
  angular.forEach(arrayOfPromises, function(promise) {
    promise.then(function(response) {
      _getnumber(response);
    })
    .catch(function(error) {
    });
  });
});

You will need to modify your $http call, so that it just returns the $http response:

return $http({  
         method : 'GET',  
         url : REST_URL+'products/nforsalebyiddisp/'+id
       });
Pop-A-Stash
  • 6,572
  • 5
  • 28
  • 54