How to set a timeout to abort an $http.get() inside a factory or service?
Asked Answered
Y

1

7

I have the following method getData(url) in a my factory which uses $http.get(url) to get data from an URL

angular
  .module('az-app')
  .factory('WebServiceFactory', function ($http, $q) {

   var WebServiceFactory = this;


   WebServiceFactory.getData = function (url) {

      var deferred = $q.defer();

      $http.get(url)
        .then(
        function (response) {

          deferred.resolve({
            data: response
          });

        }, function (rejected) {

          deferred.reject({
            data: rejected
          });
        }
      );
      //Promise to be returned
      return deferred.promise;
    }

It works fine but I need to abort the http.get and/or reject the promise so I can display an error message from my controller which has this method:

var getSpecialties = function (type) {
  doctorsCtrl.showLoading();

  var url = "example.com";

  WebServiceFactory.getData(url)
    .then(
    function (result) {
      doctorsCtrl.hideLoading();


      var specialtiesArray = result.data.data;

      StorageFactory.specialties = specialtiesArray;
      doctorsCtrl.specialties = StorageFactory.specialties

      //I WANT TO TRIGGER THIS REJECTED FUNCTION when timeout time is finished
    }, function (rejected) {
      doctorsCtrl.hideLoading();
      doctorsCtrl.showAlert();
    }
  );
}
Yerkovich answered 20/10, 2015 at 19:0 Comment(0)
S
11

The service $http accepts, in the config object, a timeout property that answers to what you need. Have a look at the documentation, especially the part about the config object:

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

Also, notice that you're using promises in an inefficient way. The following is a promise antipattern:

WebServiceFactory.getData = function (url) {

 var deferred = $q.defer();

 $http.get(url)
   .then(
   function (response) {

     deferred.resolve(...);

   }, function (rejected) {

     deferred.reject(...);
   }
 );
 //Promise to be returned
 return deferred.promise;
}

You could have simply:

WebServiceFactory.getData = function (url) {
    return $http.get(url);
}

With a timeout of 3 seconds it would be:

Service:

WebServiceFactory.getData = function (url) {
    return $http.get(url, {timeout: 3000});  // <-- timeout applies ONLY for this call
}

Controller:

WebServiceFactory.getData(url).then(
    function (result) {
      doctorsCtrl.hideLoading();
      doctorsCtrl.specialties = StorageFactory.specialties = result.data;
    }, function (rejected) {
      doctorsCtrl.hideLoading();
      doctorsCtrl.showAlert();
    }
  );

Notice also that you're calling hideLoading both in case of success and error. You can call it once, in a chained finally handler:

// ...
.finally(function () {
    doctorsCtrl.hideLoading();
}
Sullivan answered 20/10, 2015 at 19:13 Comment(10)
Hi michael, thanks for answer but I don't want to use it in config (I'm familiar with the httprovider way) as I want a timeout of 10 secs for http.get and a timeout of 60 secs for http.post methods, to my understanding If I set it in config then it is global. So I wanted an example of timeout with this http.get then I would figure it out for http.postYerkovich
It won't make it global at all, you set it in the configuration object of that specific call to $http.get. I've completed the answer with your code, hoping it's more clear.Sullivan
so I'm trying to test it with WebServiceFactory.getD = function (url) { return $http.get(url, {timeout: 1000}); }, but how can I trigger function (rejected) { at controller if the promise hasn't been resolved after 1 second have passed? I'm inserting some console.log() to see what the answer is and even though it seems that it's taking longer than 1 sec, it doesn't seem to be triggering the rejected function in controllerYerkovich
Hm, I don't see what's wrong as this is the way to do. Will make a fiddle to demonstrate.Sullivan
for example if I put WebServiceFactory.getD = function (url) { return $http.get(url, {timeout: 10}); } notice the 10 at timeout, shouldn't this be so short to trigger the reject funciton at controller since I'm making an API call that it seems its taking at least 2 secs, surely more than 1 sec. But it's not triggering the reject function :(Yerkovich
Should make the timeout resolved, and therefore trigger the error callback. I've made you a fiddleSullivan
Hi Mike this seems to be working please give me sometime to confirm if not I'll be back :)Yerkovich
at least I got it working, and you went the extra mile with the finally function so I don't have to repeat code and I can comply with DRY principle, you rock!!! thanks for all the help and patience!! will accept your answer and upvote :)Yerkovich
You're welcome! :) At the beginning promises might be a bit hard to harness but once you understand you start liking it:)Sullivan
Hi, IMHO resolve the data in the service (like @Yerkovich was doing) is a better practice, the controller doesn't need to know the respective response's object child containing the actual data.Issuable

© 2022 - 2024 — McMap. All rights reserved.