简体   繁体   中英

Break out of ajax loop

I have a function that loops through a list if items and then for each item it makes an ajax call to an external api.

This all works fine both calling it in a loop and individually. But what I want to do is to give the user the option to cancel the requests at any time. When the list is small (about <20) the process is done in about 2-3 seconds which is fine. But sometimes the list can be several hundred which can take several minutes to run and I want to give the user the option to cancel out of this at any time.

This is what I have at the moment:

<button type="button" class="btn btn-default" ng-click="getData(myList)">Get All Data</button>
<button type="button" class="btn btn-default" ng-click="cancelProcessCalls()">Get All Data</button>

<div ng-repeat="item in myList">
    <div>
        <div>{{item.Id}}</div>
        <div>{{item.Name}}</div>
        <div>
            <span ng-bind-html="item.statusText"></span>
            <span ng-bind="item.Data"></span>
        </div>
    </div>
</div>

The angular/jquery code is:

$scope.getData = function (itemList) {
    if (itemList != null) {
        $.each(itemList, function (index, item) {
            $scope.getItemData(item);
        });
    }
};

$scope.cancelProcessCalls = function () {
    $scope.processCalls=false;
};

$scope.getItemData = function (item) {
    if ($scope.processCalls) {
        if (item != null) {
            item.statusText = "Getting data...";

            $.ajax({
                url: _url + item.Id,
                method: 'GET'
            })
            .fail(function (data, textStatus, jqXHR) {
                $scope.handleError(data, textStatus, jqXHR);
            })
            .done(function (data) {
                item.Data = data;
            })
            .then(function (data, textStatus, jqXHR) {
            })
            .always(function (data, textStatus, jqXHR) {
                item.statusText = null;
                $scope.$apply();
            });
        }
    }
};

So the first function just loops through the list and makes a call for each item.

I have tried adding in a variable that checks whether to continue with the calls but that doesn't work because it is all wrapped in a scope of work.

Is there a simple way to cancel or break out of that loop elegantly?

Something like this should work. The idea is that you store the xhr object in an array and then when you want to cancel you loop around the array and call abort on the request.

$scope.requests = [];

  $scope.getData = function(itemList) {
    $scope.cancelProcessCalls();
    if (itemList != null) {
      $.each(itemList, function(index, item) {
        $scope.getItemData(item);
      });
    }
  };

  $scope.cancelProcessCalls = function() {
    for (var i = 0; i < $scope.requests.length; i++) {
      $scope.requests[i].abort();
    }

    $scope.requests.length = 0;
  };

  $scope.getItemData = function(item) {
    if ($scope.processCalls) {
      if (item != null) {
        item.statusText = "Getting data...";

        var xhr = $.ajax({
          url: _url + item.Id,
          method: 'GET'
        });
        $scope.requests.push(xhr);

        xhr.fail(function(data, textStatus, jqXHR) {
            $scope.handleError(data, textStatus, jqXHR);
          })
          .done(function(data) {
            item.Data = data;
          })
          .then(function(data, textStatus, jqXHR) {})
          .always(function(data, textStatus, jqXHR) {
            item.statusText = null;
            $scope.$apply();
          }););
    }
  }
$scope.breakCheck = false;
$scope.getData = function (itemList) {
    if (itemList != null) {
        $.each(itemList, function (index, item) {
            if ($scope.breakCheck) {
              return;
            }
            $scope.getItemData(item, $scope);
        });
    }
};

$scope.cancelProcessCalls = function () {
    $scope.processCalls=false;
};

$scope.getItemData = function (item, scope) {
    scope.breakCheck = true; // You can move this line anywhere to decide when you want to stop the ajax
    if ($scope.processCalls) {
        if (item != null) {
            item.statusText = "Getting data...";

            $.ajax({
                url: _url + item.Id,
                method: 'GET'
            })
            .fail(function (data, textStatus, jqXHR) {
                $scope.handleError(data, textStatus, jqXHR);
            })
            .done(function (data) {
                item.Data = data;
            })
            .then(function (data, textStatus, jqXHR) {
            })
            .always(function (data, textStatus, jqXHR) {
                item.statusText = null;
                $scope.$apply();
            });
        }
    }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM