[angularjs] AngularJS에서 $ http 요청을 취소하는 방법?

AngularJS에서 Ajax 요청이 주어졌습니다.

$http.get("/backend/").success(callback);

다른 요청이 시작된 경우 해당 요청을 취소하는 가장 효과적인 방법은 무엇입니까 (예 : 동일한 백엔드, 다른 매개 변수).



답변

이 기능은 타임 아웃 매개 변수를 통해 1.1.5 릴리스추가되었습니다 .

var canceler = $q.defer();
$http.get('/someUrl', {timeout: canceler.promise}).success(successCallback);
// later...
canceler.resolve();  // Aborts the $http request if it isn't finished.


답변

timeout 속성으로 Angular $ http Ajax를 취소하면 Angular 1.3.15에서 작동하지 않습니다. 이것을 고치기를 기다릴 수없는 사람들을 위해 Angular로 싸인 jQuery Ajax 솔루션을 공유하고 있습니다.

이 솔루션에는 두 가지 서비스가 포함됩니다.

  • HttpService (jQuery Ajax 함수를 감싸는 래퍼);
  • PendingRequestsService (보류중인 Ajax 요청을 추적 함)

PendingRequestsService 서비스는 다음과 같습니다.

    (function (angular) {
    'use strict';
    var app = angular.module('app');
    app.service('PendingRequestsService', ["$log", function ($log) {
        var $this = this;
        var pending = [];
        $this.add = function (request) {
            pending.push(request);
        };
        $this.remove = function (request) {
            pending = _.filter(pending, function (p) {
                return p.url !== request;
            });
        };
        $this.cancelAll = function () {
            angular.forEach(pending, function (p) {
                p.xhr.abort();
                p.deferred.reject();
            });
            pending.length = 0;
        };
    }]);})(window.angular);

HttpService 서비스 :

     (function (angular) {
        'use strict';
        var app = angular.module('app');
        app.service('HttpService', ['$http', '$q', "$log", 'PendingRequestsService', function ($http, $q, $log, pendingRequests) {
            this.post = function (url, params) {
                var deferred = $q.defer();
                var xhr = $.ASI.callMethod({
                    url: url,
                    data: params,
                    error: function() {
                        $log.log("ajax error");
                    }
                });
                pendingRequests.add({
                    url: url,
                    xhr: xhr,
                    deferred: deferred
                });
                xhr.done(function (data, textStatus, jqXhr) {
                        deferred.resolve(data);
                    })
                    .fail(function (jqXhr, textStatus, errorThrown) {
                        deferred.reject(errorThrown);
                    }).always(function (dataOrjqXhr, textStatus, jqXhrErrorThrown) {
                        //Once a request has failed or succeeded, remove it from the pending list
                        pendingRequests.remove(url);
                    });
                return deferred.promise;
            }
        }]);
    })(window.angular);

나중에 서비스에서 데이터를로드 할 때 $ http 대신 HttpService를 사용합니다.

(function (angular) {

    angular.module('app').service('dataService', ["HttpService", function (httpService) {

        this.getResources = function (params) {

            return httpService.post('/serverMethod', { param: params });

        };
    }]);

})(window.angular);

나중에 코드에서 데이터를로드하려고합니다.

(function (angular) {

var app = angular.module('app');

app.controller('YourController', ["DataService", "PendingRequestsService", function (httpService, pendingRequestsService) {

    dataService
    .getResources(params)
    .then(function (data) {
    // do stuff    
    });

    ...

    // later that day cancel requests    
    pendingRequestsService.cancelAll();
}]);

})(window.angular);


답변

$httpAngularJS의 현재 버전에서는 발행 된 요청의 취소가 지원되지 않습니다. 거기에있다 열 풀 요청 이 기능을 추가 할 수는 있지만이 AngularJS와 코어로 만들려고하는 경우가 삭제되지 않도록이 PR은 아직 검토되지 않았다.


답변

ui-router를 사용하여 stateChangeStart에서 보류중인 요청을 취소하려면 다음과 같이 사용할 수 있습니다.

// 서비스 중

                var deferred = $q.defer();
                var scope = this;
                $http.get(URL, {timeout : deferred.promise, cancel : deferred}).success(function(data){
                    //do something
                    deferred.resolve(dataUsage);
                }).error(function(){
                    deferred.reject();
                });
                return deferred.promise;

// UIrouter 설정에서

$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
    //To cancel pending request when change state
       angular.forEach($http.pendingRequests, function(request) {
          if (request.cancel && request.timeout) {
             request.cancel.resolve();
          }
       });
    });


답변

어떤 이유로 config.timeout이 작동하지 않습니다. 나는이 접근법을 사용했다 :

let cancelRequest = $q.defer();
let cancelPromise = cancelRequest.promise;

let httpPromise = $http.get(...);

$q.race({ cancelPromise, httpPromise })
    .then(function (result) {
...
});

그리고 cancelRequest.resolve ()를 취소합니다. 실제로 요청을 취소하지는 않지만 최소한 불필요한 응답을받지는 않습니다.

도움이 되었기를 바랍니다.


답변

이것은 다음과 같이 중단 방법으로 $ http 서비스를 장식하여 허용되는 답변을 향상시킵니다 …

'use strict';
angular.module('admin')
  .config(["$provide", function ($provide) {

$provide.decorator('$http', ["$delegate", "$q", function ($delegate, $q) {
  var getFn = $delegate.get;
  var cancelerMap = {};

  function getCancelerKey(method, url) {
    var formattedMethod = method.toLowerCase();
    var formattedUrl = encodeURI(url).toLowerCase().split("?")[0];
    return formattedMethod + "~" + formattedUrl;
  }

  $delegate.get = function () {
    var cancelerKey, canceler, method;
    var args = [].slice.call(arguments);
    var url = args[0];
    var config = args[1] || {};
    if (config.timeout == null) {
      method = "GET";
      cancelerKey = getCancelerKey(method, url);
      canceler = $q.defer();
      cancelerMap[cancelerKey] = canceler;
      config.timeout = canceler.promise;
      args[1] = config;
    }
    return getFn.apply(null, args);
  };

  $delegate.abort = function (request) {
    console.log("aborting");
    var cancelerKey, canceler;
    cancelerKey = getCancelerKey(request.method, request.url);
    canceler = cancelerMap[cancelerKey];

    if (canceler != null) {
      console.log("aborting", cancelerKey);

      if (request.timeout != null && typeof request.timeout !== "number") {

        canceler.resolve();
        delete cancelerMap[cancelerKey];
      }
    }
  };

  return $delegate;
}]);
  }]);

이 코드는 무엇입니까?

요청을 취소하려면 “약속”시간 초과를 설정해야합니다. HTTP 요청에 시간 초과가 설정되어 있지 않으면 코드는 “약속”시간 초과를 추가합니다. 시간 초과가 이미 설정되어 있으면 아무 것도 변경되지 않습니다.

그러나 약속을 해결하려면 “지연된”에 대한 핸들이 필요합니다. 따라서 나중에 “지연”을 검색 할 수 있도록 맵을 사용합니다. abort 메소드를 호출하면 “지연”이 맵에서 검색된 다음 resolve 메소드를 호출하여 http 요청을 취소합니다.

이것이 누군가를 돕기를 바랍니다.

한계

현재 이것은 $ http.get에서만 작동하지만 $ http.post 등에 대한 코드를 추가 할 수 있습니다.

사용하는 방법 …

그런 다음 예를 들어 다음과 같이 상태 변경시 사용할 수 있습니다 …

rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
  angular.forEach($http.pendingRequests, function (request) {
        $http.abort(request);
    });
  });


답변

다음은 여러 요청을 처리하는 버전이며 오류 블록의 오류를 억제하기 위해 콜백에서 취소 된 상태를 확인합니다. (타입 스크립트로)

컨트롤러 레벨 :

    requests = new Map<string, ng.IDeferred<{}>>();

내 http에서 :

    getSomething(): void {
        let url = '/api/someaction';
        this.cancel(url); // cancel if this url is in progress

        var req = this.$q.defer();
        this.requests.set(url, req);
        let config: ng.IRequestShortcutConfig = {
            params: { id: someId}
            , timeout: req.promise   // <--- promise to trigger cancellation
        };

        this.$http.post(url, this.getPayload(), config).then(
            promiseValue => this.updateEditor(promiseValue.data as IEditor),
            reason => {
                // if legitimate exception, show error in UI
                if (!this.isCancelled(req)) {
                    this.showError(url, reason)
                }
            },
        ).finally(() => { });
    }

도우미 방법

    cancel(url: string) {
        this.requests.forEach((req,key) => {
            if (key == url)
                req.resolve('cancelled');
        });
        this.requests.delete(url);
    }

    isCancelled(req: ng.IDeferred<{}>) {
        var p = req.promise as any; // as any because typings are missing $$state
        return p.$$state && p.$$state.value == 'cancelled';
    }

이제 네트워크 탭을 보면 잘 작동합니다. 나는 방법을 4 번 호출했으며 마지막 방법 만 통과했습니다.

여기에 이미지 설명을 입력하십시오