AngularJS'de bir $ http isteği nasıl iptal edilir?


190

AngularJS'de bir Ajax isteği verildi

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

başka bir istek başlatılırsa bu isteği iptal etmenin en etkili yolu nedir (örneğin, aynı arka uç, farklı parametreler).


8
Aşağıdaki cevapların hiçbiri aslında isteğin kendisini iptal etmez. Bir HTTP isteğini tarayıcıdan ayrıldıktan sonra iptal etmenin bir yolu yoktur. Aşağıdaki tüm cevaplar dinleyiciyi bir şekilde terk eder. HTTP isteği hala sunucuya vurur, yine de işlenir ve sunucu yine de bir yanıt gönderir, bu sadece istemcinin bu yanıt için hala lizeli olup olmadığı durumudur.
Liam

$ http isteği rota değişikliğinde iptal freakyjolly.com/how-to-cancel-http-requests-in-angularjs-app
Code Spy


@Liam sorum sunucuda iptal edilmiyordu. bu, sunucu teknolojinizin / uygulamanızın ne olduğuna çok özgüdür. Geri arama terk ile ilgili
Sonic Soul

Yanıtlar:


326

Bu özellik, bir zaman aşımı parametresi aracılığıyla 1.1.5 sürümüne eklendi :

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
Hem zaman aşımına hem de sözlü olarak manuel olarak iptal etmeye ihtiyacım olursa ne yapmalıyım?
Raman Chodźka

15
@ RamanChodźka Her ikisini de bir söz ile yapabilirsiniz; JavaScript'in yerel setTimeoutişleviyle veya Angular $timeouthizmetiyle belirli bir süre sonra vaadi iptal etmek için bir zaman aşımı ayarlayabilirsiniz .
Quinn Strahl

9
canceler.resolve () gelecekteki istekleri iptal edecektir. Bu daha iyi bir çözümdür: odetocode.com/blogs/scott/archive/2014/04/24/…
Toolkit

7
Ben Nadel'den
Pete

3
Gerçekten çalışmıyor. Çalışan bir örnek verebilir misiniz?
Edward Olamisan

10

Açısal $ http Ajax zaman aşımı özelliğiyle iptal edildiğinde Açısal 1.3.15'te çalışmaz. Bunun düzeltilmesini bekleyemeyenler için Angular'a sarılmış bir jQuery Ajax çözümü paylaşıyorum.

Çözüm iki hizmeti içerir:

  • HttpService (jQuery Ajax işlevi çevresinde bir sarıcı);
  • PendingRequestsService (bekleyen / açık Ajax isteklerini izler)

İşte PendingRequestsService hizmeti:

    (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 hizmeti:

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

Daha sonra hizmetinizde veri yüklerken $ http yerine HttpService kullanırsınız:

(function (angular) {

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

        this.getResources = function (params) {

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

        };
    }]);

})(window.angular);

Daha sonra kodunuzda verileri yüklemek istersiniz:

(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

İle yapılan isteklerin iptali, $httpAngularJS'nin geçerli sürümünde desteklenmez. Bu özelliği eklemek için bir çekme isteği açıldı , ancak bu PR henüz gözden geçirilmedi, bu yüzden AngularJS çekirdeği haline getirip getirmeyeceği belli değil.


PR'nin reddedildiğini, OP burada güncellenen birini gönderdi github.com/angular/angular.js/pull/1836
Mark Nadig

Ve bu da kapalıydı.
frapontillo

Bir versiyonu böyle çıktı . Yine de son sürümü kullanmak için sözdizimini anlamaya çalışıyor. PR'lerin kullanım örnekleri ile gelmesini diliyorum! :)
SimplGy

'Kullanım' bölümündeki docs.angularjs.org/api/ng/service/$http açısal dokümantasyon sayfası bir zaman aşımı ayarını açıklar ve ayrıca hangi nesnelerin (bir Söz) kabul edildiğinden bahseder.
Igor Lino

6

StateChangeStart üzerinde bekleyen istekleri ui-router ile iptal etmek isterseniz, şöyle bir şey kullanabilirsiniz:

// serviste

                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 yapılandırmasında

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

Bu benim için çalıştı - çok basit ve ben aramayı seçmek ve sadece bazı çağrıları iptal edebilirsiniz böylece aramayı adlandırmak için başka bir ekledi
Simon Dragsbæk

UI Router yapılandırmasının neden mevcut olduğunu bilmesi gerekiyor request.timeout?
trysis

6

Herhangi bir nedenle config.timeout benim için çalışmıyor. Bu yaklaşımı kullandım:

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

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

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

Ve iptal etmek için cancelRequest.resolve (). Aslında bir isteği iptal etmez ancak en azından gereksiz yanıt almazsınız.

Bu yardımcı olur umarım.


SyntaxError'ınızı gördünüz mü { cancelPromise, httpPromise }?
Mephiztopheles

bu ES6 sözdizimi, deneyebilirsiniz {c: cancelPromise, h: httpPromise}
Aliaksandr Hmyrak

Anlıyorum, nesne
shortinitializer

3

Bu, $ http hizmetini bir iptal yöntemi ile aşağıdaki şekilde süsleyerek kabul edilen cevabı geliştirir ...

'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;
}]);
  }]);

BU KOD NELER YAPIYOR?

Bir isteği iptal etmek için bir "vaat" zaman aşımı ayarlanmalıdır. HTTP isteğinde herhangi bir zaman aşımı ayarlanmamışsa, kod bir "vaat" zaman aşımı ekler. (Zaten bir zaman aşımı ayarlanmışsa, hiçbir şey değiştirilmez).

Ancak, vaadi çözmek için "ertelenmiş" bir ele ihtiyacımız var. Böylece "ertelenmiş" i daha sonra alabilmemiz için bir harita kullanıyoruz. Durdurma yöntemini çağırdığımızda, "ertelenmiş" haritadan alınır ve daha sonra http isteğini iptal etmek için çözümleme yöntemini çağırırız.

Umarım bu birine yardımcı olur.

SINIRLAMA

Şu anda bu yalnızca $ http.get için çalışıyor ancak $ http.post vb. İçin kod ekleyebilirsiniz.

NASIL KULLANILIR ...

Daha sonra, örneğin durum değişikliğinde aşağıdaki gibi kullanabilirsiniz ...

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

Bazı http isteklerini aynı anda tetikleyen bir uygulama yapıyorum ve hepsini manuel olarak iptal etmem gerekiyor. Kodunuzu denedim ancak yalnızca son isteği iptal ediyor. Bu daha önce sana oldu mu? Herhangi bir yardım mutluluk duyacağız.
Miguel Trabajo

1
buradaki kod, erteleme nesnelerine bir başvuru yapmak için ertelemeyi gerektirdiğinden, erteleme nesnelerine yapılan başvuruların aramasını sağlar. aramayla ilgili önemli olan şey anahtar: değer çifti. Değer erteleme nesnesidir. Anahtar, request yöntemine / url'ye göre oluşturulan bir dizedir. Aynı yöntem / url için birden fazla istek iptal edildiğini tahmin ediyorum. Bu nedenle tüm tuşlar aynıdır ve haritada birbirlerinin üzerine yazılır. Url / yöntemi aynı olsa bile benzersiz bir tane oluşturulabilmesi için anahtar oluşturma mantığını değiştirmelisiniz.
danday74

1
yukarıdan devam etti ... bu kodda bir hata değil, kod birden fazla isteği iptal ediyor ... ama kod asla aynı http yöntemini kullanarak aynı url'ye birden fazla isteği iptal etmekle uğraşmak değildi ... mantığı değiştirirseniz, onu kolayca çalıştırabilmeniz gerekir.
danday74

1
Çok teşekkür ederim! Aynı URL'ye birden fazla istekte bulunuyordum, ancak farklı parametrelerle, ve bunu söyledikten sonra bu satırı değiştirdim ve bir cazibe gibi çalıştı!
Miguel Trabajo

1

burada birden fazla isteği işleyen bir sürüm, ayrıca hata bloğundaki hataları bastırmak için geri çağrıda iptal edilen durumu kontrol eder. (Daktilo yazısında)

kontrolör seviyesi:

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

benim http olsun:

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

yardımcı yöntemler

    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';
    }

şimdi ağ sekmesine bakarak, güzelce çalıştığını görüyorum. yöntemi 4 kez çağırdı ve sadece sonuncusu geçti.

resim açıklamasını buraya girin


req.resolve ( 'iptal'); benim için çalışmıyor, 1.7.2 sürümünü kullanıyorum. Hatta ben tekrar çağrılır ve ilk çağrı hala bekleyen durumunda bir çağrıyı iptal etmek istiyorum. lütfen yardım et. her zaman aynı
url'nin

1

Bu işlevi sözlerinize $httpekleyecek bir "dekoratör" kullanarak hizmete özel bir işlev ekleyebilirsiniz abort().

İşte bazı çalışma kodu:

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

Bu kod, hizmete bir with_abort()işlev eklemek için angularjs dekoratör işlevini kullanır $http.

with_abort()$httphttp isteğini iptal etmenizi sağlayan zaman aşımı seçeneğini kullanır .

İade edilen söz bir abort() işlevi . Ayrıca abort()zincir vaat bile çalışır çalışır emin olmak için kodu vardır .

İşte nasıl kullanacağınıza bir örnek:

// 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

Varsayılan olarak aradığınızda abort() istek iptal edilir ve söz vericilerin hiçbiri çalışmaz.

Hata işleyicilerinizin true olarak adlandırılmasını istiyorsanız abort(true) .

Hata işleyicinizde özelliği kontrol ederek "hatanın" bir "iptal" olup olmadığını kontrol edebilirsiniz xhrStatus. İşte bir örnek:

var promise = $http.with_abort({ method: 'GET', url: '/names' }).then(
    function(names) {
        do_something(names));
    }, 
    function(error) {
        if (er.xhrStatus === "abort") return;
    });
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.