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


190

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

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

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


8
아래의 답변 중 어느 것도 실제로 요청 자체를 취소하지 않습니다. 브라우저를 떠나면 HTTP 요청을 취소 할 수 없습니다. 아래의 모든 답변은 단순히 청취자를 어떤 식 으로든 결합시킵니다. HTTP 요청은 여전히 ​​서버에 도달하고 처리되며 서버는 여전히 응답을 보냅니다. 클라이언트가 여전히 해당 응답에 대해 lisening하는 경우입니다.
Liam

경로 변경에 대한 $ http 요청 취소 freakyjolly.com/how-to-cancel-http-requests-in-angularjs-app
Code Spy


@ Liam 내 질문이 서버에서 취소되지 않았습니다. 그것은 귀하의 서버 기술 / 구현에 대해 매우 구체적 일 것입니다. 나는 콜백을 포기하는 것에 관심이 있었다
Sonic Soul

답변:


326

이 기능은 타임 아웃 매개 변수를 통해 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.

14
약속을 통해 시간 제한과 수동 취소가 모두 필요한 경우 어떻게해야합니까?
Raman Chodźka

15
@ RamanChodźka 약속으로 둘 다 할 수 있습니다. JavaScript의 기본 setTimeout기능 또는 Angular의 $timeout서비스를 사용하여 일정 시간이 지난 후 약속을 취소하도록 시간 초과를 설정할 수 있습니다 .
Quinn Strahl

9
canceler.resolve ()는 향후 요청을 취소합니다. 이것은 더 나은 해결책입니다 : odetocode.com/blogs/scott/archive/2014/04/24/…
Toolkit

7
Ben Nadel의보다 완벽한 솔루션의 또 다른 좋은 예 : bennadel.com/blog/…
Pete

3
실제로 작동하지 않습니다. 작업 샘플을 제공 할 수 있습니까?
Edward Olamisan

10

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);

9

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


PR이 거부 된 것을, 영업 이익은 여기에서 업데이트 제출 된 github.com/angular/angular.js/pull/1836
마크 Nadig

그리고 그것은 폐쇄되었습니다.
frapontillo

그것의 버전은 이것으로 착륙했습니다 . 최종 버전을 사용하기 위해 구문을 파악하려고 여전히 시도합니다. PR에 사용 샘플이 함께 제공되기를 바랍니다! :)
SimplGy

'Usage' 의 앵귤러 문서 페이지 docs.angularjs.org/api/ng/service/$http 는 시간 초과 설정을 설명하고 어떤 개체 (약속)가 허용되는지도 언급합니다.
Igor Lino

6

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();
          }
       });
    });

이것은 나를 위해 일했습니다-매우 간단하고 통화 이름을 지정하기 위해 다른 통화를 추가했습니다. 그래서 통화를 선택하고 일부 통화 만 취소 할 수 있습니다
Simon Dragsbæk

UI 라우터 구성 request.timeout이 존재 하는 이유를 알아야하는 이유는 무엇 입니까?
trysis

6

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

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

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

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

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

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


SyntaxError 보이 { cancelPromise, httpPromise }십니까?
Mephiztopheles

이것은 ES6 구문입니다. {c : cancelPromise, h : httpPromise}
Aliaksandr Hmyrak 님에게

나는 객체 shortinitializer
Mephiztopheles

3

이것은 다음과 같이 중단 방법으로 $ 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);
    });
  });

동시에 일부 HTTP 요청을 발생시키는 앱을 만들고 있으며 수동으로 중단해야합니다. 코드를 시도했지만 마지막 요청 만 중단됩니다. 전에 그랬어? 도움을 주시면 감사하겠습니다.
Miguel Trabajo

1
여기서 코드는 지연 개체가 중단을 수행해야하므로 나중에 검색 할 수 있도록 지연 개체에 대한 참조 조회를 유지 관리합니다. 조회에서 중요한 것은 key : value 쌍입니다. 값은 지연 오브젝트입니다. 키는 요청 메소드 / url을 기반으로 생성 된 문자열입니다. 동일한 메소드 / url에 여러 요청을 중단하고 있다고 생각합니다. 이 때문에 모든 키는 동일하며 맵에서 서로 덮어 씁니다. URL / 메소드가 동일한 경우에도 고유 한 로직이 생성되도록 키 생성 로직을 조정해야합니다.
danday74

1
위에서 계속 ... 이것은 코드의 버그가 아니며 코드는 여러 요청을 중단 처리합니다 ...하지만 코드는 단순히 동일한 http 메소드를 사용하여 동일한 URL에 여러 요청을 중단시키는 것을 결코 의미하지 않았습니다 ... 로직을 조정하면 상당히 쉽게 작동 할 수 있어야합니다.
danday74

1
대단히 감사합니다! 나는 같은 URL에 여러 요청을했지만 다른 매개 변수를 사용했으며, 당신이 그것에 대해 말한 후에 그 줄을 변경하면 매력처럼 작동했습니다!
Miguel Trabajo

1

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

컨트롤러 레벨 :

    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 번 호출했으며 마지막 방법 만 통과했습니다.

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


req.resolve ( '취소'); 1.7.2 버전을 사용하고 있습니다. 다시 전화를 걸고 첫 번째 통화가 여전히 보류 상태에 있으면 통화를 취소하고 싶습니다. 도와주세요. 난 항상 같은 URL의 보류중인 모든 API를 취소하여 새로 호출 된 통화 데이터를 제공하고 싶습니다
Sudarshan Kalebere

1

약속에 기능을 $http추가하는 "데코레이터"를 사용 하여 서비스에 사용자 정의 기능을 추가 할 수 있습니다 abort().

작동 코드는 다음과 같습니다.

app.config(function($provide) {
    $provide.decorator('$http', function $logDecorator($delegate, $q) {
        $delegate.with_abort = function(options) {
            let abort_defer = $q.defer();
            let new_options = angular.copy(options);
            new_options.timeout = abort_defer.promise;
            let do_throw_error = false;

            let http_promise = $delegate(new_options).then(
                response => response, 
                error => {
                    if(do_throw_error) return $q.reject(error);
                    return $q(() => null); // prevent promise chain propagation
                });

            let real_then = http_promise.then;
            let then_function = function () { 
                return mod_promise(real_then.apply(this, arguments)); 
            };

            function mod_promise(promise) {
                promise.then = then_function;
                promise.abort = (do_throw_error_param = false) => {
                    do_throw_error = do_throw_error_param;
                    abort_defer.resolve();
                };
                return promise;
            }

            return mod_promise(http_promise);
        }

        return $delegate;
    });
});

이 코드는 angularjs의 데코레이터 기능을 사용 with_abort()하여$http 서비스에 .

with_abort() 사용 $http 는 HTTP 요청을 취소 할 수 있습니다 옵션 타임 아웃.

반환 된 약속은 abort()함수 를 포함하도록 수정되었습니다 . 또한 코드를 확인하여abort() 약속을 묶어도 작동 있습니다.

사용 방법의 예는 다음과 같습니다.

// your original code
$http({ method: 'GET', url: '/names' }).then(names => {
    do_something(names));
});

// new code with ability to abort
var promise = $http.with_abort({ method: 'GET', url: '/names' }).then(
    function(names) {
        do_something(names));
    });

promise.abort(); // if you want to abort

전화 할 때 기본적으로 abort() 요청하면 요청이 취소되고 약속 핸들러가 실행되지 않습니다.

오류 처리기를 호출하려면 true를 전달하십시오. abort(true) .

오류 처리기에서 xhrStatus속성 을 확인하여 "오류"가 "중단"으로 인한 것인지 확인할 수 있습니다 . 예를 들면 다음과 같습니다.

var promise = $http.with_abort({ method: 'GET', url: '/names' }).then(
    function(names) {
        do_something(names));
    }, 
    function(error) {
        if (er.xhrStatus === "abort") return;
    });
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.