4

I'm trying to update a progress bar with a sequential http request inside a foreach, this works, but it's not synchronous by on complete, progress bar is being sync'd by the http call, what am I doing wrong?

angular.forEach(queue, function (item) {
    if (item.uid) {
        d.item = item;
        $http({
            url: BASE_URL + 'upp', 
            method: 'post',
            data: d                  
        }).then(function(res){
            x++;
            update_progress((x/queue_count)*100);
        });
    }
});

I would like to call the update_progress function just when the http returns as finished (200 OK), so the progress bar shows the actual progress correctly. Thanks!

Edit:

I tried checking the response status, before calling the *update_progress* function and it still does not work as expected. I wasn't aware that 200 was dispatched before the request is completed :| By logic, the res obj shouldn't be the response of the http request? I mean, if it's 200 and not an error code, that shouldn't mean that the request was completed?

angular.forEach(queue, function (item) {
if (item.uid) {
    d.item = item;
    $http({
        url: BASE_URL + 'upp', 
        method: 'post',
        data: d                  
    }).then(function(res){
        if(res.status == 200) {
          x++;
          update_progress((x/queue_count)*100);
        }
    });
}

Reading more on promises atm to see if I can make it work as stated by @josh-strange

Edit 2:

So promises was the way to do it, all requests are sent sequentially so the progress bar works as expected, here's the code:

var promise = $q.all({}); 
// Had to init $q.all with an empty obj, 'cause null was trowing errors

angular.forEach(queue, function(item){
  if (item.uid) {        
    promise = promise.then(function(){
      d.item = item;
      return $http({
        url: BASE_URL + 'upp', 
        method: 'post',
        data: d 
      }).then(function(res){
        x++;
        update_progress((x/queue_count)*100);
      });
    });
  }
});

promise.then(function(){
  end_js_update();
});

Thanks @josh-strange

Antonio Max
  • 8,627
  • 6
  • 43
  • 42
  • Don't use forEach, manually loop through queue? Not sure that will work either. Async can be such a pain sometimes. – aet Sep 26 '13 at 20:10
  • You [cannot](http://stackoverflow.com/questions/13088153/how-to-http-synchronous-call-with-angularjs) make synchronous calls in AngularJS but you can make sequential http calls (Reg 1, then Reg 2, etc..). Let me try to get a Plunker together for you. – JoshStrange Sep 26 '13 at 20:32
  • [Here](http://plnkr.co/edit/HMccomnJooWpIHFMO4H8) is an VERY simple example of sequential HTTP requests, I will post an answer in a minute with it cleaned up. – JoshStrange Sep 26 '13 at 20:47
  • @AntonioMax Does my answer below solve you problem? – JoshStrange Sep 27 '13 at 13:10
  • @josh-strange trying to implement the logic on my code. Gonna update & accept if works as expected. Never had to create a promise before, so I'm reading the docs. – Antonio Max Sep 27 '13 at 18:32

3 Answers3

13

Here is a working Plunker of a working example of sequential http requests. You could obviously package this up very nicely in a service but for your purposes I just put a simple example of it in a controller.

Here is the "meat" of the code:

var app = angular.module('testApp',[]);

app.controller('myController', function($scope, $http, $q){

  $scope.responses = [];
  $scope.doneLoading = false;
  var urls = [
    'http://httpbin.org/ip',
    'http://httpbin.org/user-agent',
    'http://httpbin.org/headers'
  ];

  var promise = $q.all(null);

  angular.forEach(urls, function(url){
    promise = promise.then(function(){
      return $http({
        method: 'GET', 
        url:url 
      }).then(function(res){
        $scope.responses.push(res.data);
      });
    });
  });

  promise.then(function(){
    //This is run after all of your HTTP requests are done
    $scope.doneLoading = true;
  })

});

EDIT: As Mike P says below: This is an example of chaining promises see $q documentation.

JoshStrange
  • 1,121
  • 1
  • 7
  • 22
0

You could try something like this. Just wait to send the next request until the previous is finished?

var sendRequest = function(index) {
  if (queue[index].uid) {
    d.item = queue[index];
    $http({
      url: BASE_URL + 'upp', 
      method: 'post',
      data: d                  
    }).then(function(res){
      if (index != queue.length - 1) {
        x++;
        update_progress((x / queue_count) * 100);
        sendRequest(++index);
      }
    });
  }
}
Zack Argyle
  • 8,057
  • 4
  • 29
  • 37
  • thank you, but @josh-strange answer was better because it uses promises, which I believe now is the right way to do it! – Antonio Max Sep 27 '13 at 20:38
0
<button ng-click="vm.startFetchingKeysAndAnswers(vm.selectedTestReader.serial,vm.selectedTestReader.Type)"
    ng-if="vm.isShowStartFetchingbtn==true" id="btnStartFetching"
    type="button" class="btn btn-primary btn-font" style="/*margin-left: 20px;*/">
    Start
</button>
<div class="progress" style="margin-top: 10px;">
    <div id="progressBar" class="progress-bar progress-bar-striped active" role="progressbar"
        aria-valuemin="0" aria-valuemax="100">
        60 %
    </div>
</div>



function startFetchingKeysAndAnswers(testReaderId, type) {
    vm.fetchFailedResultKeysAndAnswers = [];
    vm.apiCallCount = 0;
    vm.isShowStartFetchingbtn == false;
    document.getElementById("btnStartFetching").style.display = "none";
    let errorCount = 0;
    var promise = $q.all({});
    angular.forEach(vm.studentForFetchKeysAndAnswers, function (item) {
        promise = promise.then(function () {
            let userName = type == 2 ? item.Gozine2UserName : KanoonUserName;
            let password = type == 2 ? item.Gozine2Password : KanoonPassword;
            return testReaderService.getKeysAndAnswers(testReaderId, userName, password, item.NationalNo, type)
                .then(function (data) {
                    if (data.data.HasError) {
                        errorCount++;
                        vm.fetchFailedResultKeysAndAnswers.push(item);
                    }
                    vm.apiCallCount++;
                    let percent = vm.apiCallCount / vm.studentForFetchKeysAndAnswers.length * 100;
                    document.getElementById("progressBar").setAttribute("style", "width:" + percent + "%");
                    document.getElementById("progressBar").innerHTML = percent + "%";
                })
        });
    });
    promise.then(function () {

    });
}
M Komaei
  • 7,006
  • 2
  • 28
  • 34