13

Have a look into the code snippet

$scope.getSongs = function(keyword){
     songServices.getSongList(keyword).then(
         function(resp){
             $scope.songList = resp.data.songList;
         }
     );
}

Here getSongList simply returns list of songs from server by an HTTP request.

And in my HTML:

<input auto-focus type="text" placeholder="Enter song ID/Keyword" ng-model="keyword" ng-change="getSongs()">

The problem here is with behaviour of promises, sometimes if some promise takes more time(even in ms.) to get resolved then it shows false data. when you search for 'AKON' lets say promise with first strike 'A' returns last then it refreshes the scope with false data, Is there any way to stop or discard promise which have not been resolved before sending another promise to server, or how can I handle such kind of scenario.

Thanks in advance.

Mike Chamberlain
  • 39,692
  • 27
  • 110
  • 158
Suraj Khurana
  • 392
  • 3
  • 14
  • without going into enough detail for an answer, you can chain a promise as an intermediary that can be used to reject outdated promises. – zzzzBov Jun 12 '15 at 17:46

3 Answers3

10

$http calls can be cancelled, by passing a promise in the 'timeout' config option, and resolving that promise.

From the documentation:

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

Example:

var canceler = $q.defer();
$http.get(someUrl, { timeout: canceler.promise });

// later: cancel the http request

canceler.resolve();
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
6

Rather than destroy the promise it might be better not to make a call until the user has stopped typing. you can use the ng-options directive to set a debounce timer. That way the action will only get executed once the user has stopped typing.

<input auto-focus type="text" placeholder="Enter song ID/Keyword" ng-model="keyword" ng-change="getSongs" ng-model-options="{ debounce: 500}">

Kent Cooper
  • 4,319
  • 3
  • 19
  • 23
  • 1
    Instead of "rather", I'd say "In addition to". Even if the second request is sent 500ms. after a first one, its response might come earlier. – JB Nizet Jun 12 '15 at 14:16
  • The problem is we don't know how abstracted the $http service is based on the code example provided. On top of that the angular team decided not to implement an actual [cancel api](https://github.com/angular/angular.js/pull/2452) on $q promises. The easiest thing for him to do would be to return the keyword value that was searched for by his service and check it against the current keyword value on the $scope and if they don't match then don't update the list. – Kent Cooper Jun 12 '15 at 16:06
2

You could create a destroyable promise from an ordinary promise easily enough:

var destroyablePromise = function(p) {
  var r = $q.defer();

  p.then(function(a) { if (p) { r.resolve(a); }}, 
         function(a) { if (p) { r.reject(a); }},
         function(a) { if (p) { r.notify(a); }});

  r.promise.destroy = function() {
    p = null;
  };
  return r.promise;
}

Et voilà!

Michael Lorton
  • 43,060
  • 26
  • 103
  • 144
  • I like this solution, initially I thought I would need to reject the promise to cancel it, but this is event better, nothing will ever happen! Is there a risk of memory leaks with this? – pauloya Aug 31 '16 at 14:56
  • There isn't a risk of a _new_ memory leak. Obviously, if you leak `r`, you leak `p` too. – Michael Lorton Aug 31 '16 at 15:44