Why and how to abort an ongoing angular $http request

Long time ago I wrote a blog about aborting ongoing ajax request in jQuery here Why and how to abort an ajax call in jQuery.

In this post we will do the same using angularjs. Before going into how we do it lets discuss few things about why we need it.

Lets take a simple example, we need a search functionality. For this we take an input field and attach a keyup event on which we will make a $http request to server with typed text and get the result.

<input type=”text” ng-model=”search” ng-keyup=”search()”>

Create a service function for http request and call the service function in controller like

service.js

service.search = function(searchString){
    $http.post(url,{search:searchString}).then(
        function(response){
            //success callback
        }, function(response){
            //error callback
        }
    );
};

 

controller.js

$scope.search = '';

$scope.search = function(){
    searchService.search($scope.search);
};

 

These code will be well enough to implement simple search functionality.

But there is a problem, if you want to search something like ‘angular’ then there will be 7 http requests in place because search function called on keyup event only. So one call for ‘a’ then another for ‘an’ then another for ‘ang’ and so on.

Now these  are asynchronous calls and very much unpredictable. You can’t predict in which sequence request will be completed, its never guranted that the sequence you made the request, result/response will come in same sequence. It may happen that ajax request with ‘angular’  gets finished before the request for ‘angula’ gets over. Though the request for ‘angula’ finished last so user will see the result for ‘angula’ even if search keyword is ‘angular’ which is very bad user experience.

To overcome this, we need to abort/cancel the previous request is in process before we make a new request. To achieve this we will take help of angular $q service, $q is a service that helps you run functions asynchronously, and use their return values (or exceptions) when they are done processing.

We will create a defer object using $q and will take a variable to have the status of ongoing ajax request. By default we will take that value as false and will set to true when an ajax started and will set it back to false when ajax completes. So anytime if that variable returns true  then we will understand there is some ongoing ajax call which we need to cancel.

$http request’s config parameter has a timeout option through which we can timeout an ajax request.

timeout – {number|Promise} – timeout in milliseconds, or promise that should abort the request when resolved.

So each and every request we will create a promise and before initiating another call we will check if any ongoing request, if so then we will resolve the promise of that request which will automatically abort or timeout that request.

So the new service function will be like

var cancelSearch = $q.defer(); //defer object
var searchResolve = false; //status of a request

service.search = function(searchString){
    //checking for any other ongoing call
    if(searchResolve){
       //resolving previous request's defer which will abort that ajax call
       cancelSearch.resolve('search aborted');
    }

    cancelSearch = $q.defer(); //create new defer for new request
    searchResolve = true; //set the request status to true/ongoing

    $http.post(url,{search:searchString},{timeout:cancelSearch.promise})
    .then(
        function(response){
            //success callback
            searchResolve = false; //set the request status to completed
        }, function(response){
            //error callback
        }
    );
};

 

NOTE: Remember to inject $q service to your controller/service /directive

 

 

Leave a comment