mart1n
mart1n

Reputation: 6213

Executing command after forEach finishes in Angular

I have the following function that gets executed from an ng-click in the HTML:

vm.items = [];
vm.moveItems = function() {
    angular.forEach(vm.items,
        function (item) {
            $http({
                method: 'PUT',
                url: '/api/item_move/' + item.id}
            }).then(function(response) {
                Toast.success("Successfully moved item " + item.id);
                vm.reload();
            }, function (error) {
                Toast.error("Failed to move item " + item.id, error);
            });
    });
};

Now, the problem is that the vm.reload() is executed after every successful response where in fact it would be enough if it was executed once after the entire forEach finishes. I'm very new to async programming in JS so would like to know what the most used way to solve this is.

Upvotes: 0

Views: 1431

Answers (4)

Divyansh Kumar
Divyansh Kumar

Reputation: 26

Solution:

vm.items = [];

vm.moveItems = function() {
    var promises = [];

    angular.forEach(vm.items , function(item) {

        var promise = $http({
            method: 'PUT',
            url: '/api/item_move/' + item.id}
        }).then(function(response) {
            Toast.success("Successfully moved item " + item.id);
        }, function (error) {
            Toast.error("Failed to move item " + item.id, error);
        });

        promises.push(promise);

   });

   $q.all(promises).then(function() { vm.reload() })
});

Upvotes: 0

JSON Derulo
JSON Derulo

Reputation: 17578

Create an array where you store the promises from your HTTP calls. Afterwards call the Promise.all() method. This allows you to do stuff when all promises are finished.

vm.items = [];
vm.moveItems = function() {
    var promises = [];
    angular.forEach(vm.items,
        function (item) {
            promises.push($http({
                method: 'PUT',
                url: '/api/item_move/' + item.id}
            }));
    });
    Promise.all(promises)
        .then(function() {
            vm.reload();
        });
};

Edit: Since you are using AngularJS, you can also do $q.all().

Upvotes: 2

rrd
rrd

Reputation: 5957

Move the vm.reload() outside the forEach loop:

vm.items = [];
vm.moveItems = function() {
  this._$timeout(() => {

    angular.forEach(vm.items,
      function (item) {
        $http({
          method: 'PUT',
          url: '/api/item_move/' + item.id}
        }).then(function(response) {
          Toast.success("Successfully moved item " + item.id);
        }, function (error) {
          Toast.error("Failed to move item " + item.id, error);
        });
    });

  });

  vm.reload();
};

Edited to add $timeout, should give it enough time to finish the foreach loop.

Upvotes: -1

Gaurav Srivastava
Gaurav Srivastava

Reputation: 3232

You could do something like this:

vm.items = [];
vm.moveItems = function() {
var i = 0 
    angular.forEach(vm.items,
        function (item) {
            $http({
                method: 'PUT',
                url: '/api/item_move/' + item.id}
            }).then(function(response) {
                Toast.success("Successfully moved item " + item.id);
                i++;
                if(i == vm.items.length{ // execute only when its the last loop of foreach
                     vm.reload(); 
                }

            }, function (error) {
                i++;
              if(i == vm.items.length{ // execute only when its the last loop of foreach
                     vm.reload(); 
                }
                Toast.error("Failed to move item " + item.id, error);
            });
    });
};

Upvotes: 0

Related Questions