Bagaimana cara membatalkan permintaan $ http di AngularJS?


190

Diberikan permintaan Ajax di AngularJS

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

apa cara paling efektif untuk membatalkan permintaan itu jika permintaan lain diluncurkan (backend yang sama, parameter yang berbeda misalnya).


8
Tidak ada jawaban di bawah ini yang benar-benar membatalkan permintaan itu sendiri. Tidak ada cara untuk membatalkan permintaan HTTP setelah meninggalkan browser. Semua jawaban di bawah ini cukup dari luar pendengar dalam beberapa cara. Permintaan HTTP masih mengenai server, masih diproses dan server masih akan mengirim respons, hanya kasus apakah klien masih mendengarkan atau tidak.
Liam

$ http permintaan dibatalkan pada perubahan rute freakyjolly.com/how-to-cancel-http-requests-in-angularjs-app
Code Spy

kode untuk promise.abort() stackoverflow.com/a/50415480/984780
Luis Perez

@Liam pertanyaan saya tidak dibatalkan di server. itu akan sangat spesifik untuk apa teknologi / implementasi server Anda. saya khawatir dengan meninggalkan panggilan balik
Sonic Soul

Jawaban:


326

Fitur ini ditambahkan ke rilis 1.1.5 melalui parameter batas waktu:

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
apa yang harus saya lakukan jika saya perlu waktu tunggu dan pembatalan manual melalui janji?
Raman Chodźka

15
@ RamanChodźka Anda bisa melakukan keduanya dengan janji; Anda dapat menetapkan batas waktu untuk membatalkan janji setelah beberapa waktu, baik dengan setTimeoutfungsi asli JavaScript atau layanan Angular $timeout.
Quinn Strahl

9
canceler.resolve () akan membatalkan permintaan di masa mendatang. Ini adalah solusi yang lebih baik: odetocode.com/blogs/scott/archive/2014/04/24/…
Toolkit

7
contoh bagus lain dari solusi yang lebih lengkap dari Ben Nadel: bennadel.com/blog/…
Pete

3
Tidak benar-benar berfungsi. Bisakah Anda memberikan sampel yang berfungsi?
Edward Olamisan

10

Membatalkan Angular $ http Ajax dengan properti timeout tidak berfungsi di Angular 1.3.15. Bagi mereka yang tidak sabar untuk memperbaikinya, saya membagikan solusi jQuery Ajax yang dibungkus dengan Angular.

Solusinya melibatkan dua layanan:

  • HttpService (pembungkus di sekitar fungsi jQuery Ajax);
  • PendingRequestsService (melacak permintaan Ajax yang tertunda / terbuka)

Ini dia layanan 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);

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

Kemudian dalam layanan Anda saat Anda memuat data, Anda akan menggunakan HttpService alih-alih $ http:

(function (angular) {

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

        this.getResources = function (params) {

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

        };
    }]);

})(window.angular);

Kemudian dalam kode Anda, Anda ingin memuat data:

(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

Pembatalan permintaan yang dikeluarkan dengan $httptidak didukung dengan versi AngularJS saat ini. Ada permintaan tarik terbuka untuk menambahkan kemampuan ini tetapi PR ini belum ditinjau sehingga tidak jelas apakah akan membuatnya menjadi inti AngularJS.


bahwa PR ditolak, OP menyerahkan yang diperbarui di sini github.com/angular/angular.js/pull/1836
Mark Nadig

Dan itu ditutup juga.
frapontillo

Versi itu mendarat seperti ini . Masih mencoba mencari tahu sintaks untuk menggunakan versi final. Berharap PR datang dengan sampel penggunaan! :)
SimplGy

Halaman dokumentasi sudut docs.angularjs.org/api/ng/service/$http di 'Penggunaan' menjelaskan pengaturan batas waktu, dan juga menyebutkan objek apa (Janji) yang diterima.
Igor Lino

6

Jika Anda ingin membatalkan permintaan yang tertunda pada stateChangeStart dengan ui-router, Anda dapat menggunakan sesuatu seperti ini:

// dalam pelayanan

                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;

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

Ini bekerja untuk saya - sangat sederhana dan saya menambahkan satu lagi untuk memberi nama panggilan sehingga saya dapat memilih panggilan dan hanya membatalkan beberapa panggilan
Simon Dragsbæk

Mengapa konfigurasi Router UI perlu tahu jika request.timeoutada?
trysis

6

Untuk beberapa alasan config.timeout tidak berfungsi untuk saya. Saya menggunakan pendekatan ini:

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

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

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

Dan cancelRequest.resolve () untuk membatalkan. Sebenarnya itu tidak membatalkan permintaan tetapi Anda tidak mendapatkan respons yang tidak perlu.

Semoga ini membantu.


Apakah Anda melihat SyntaxError Anda { cancelPromise, httpPromise }?
Mephiztopheles

ini adalah sintaks ES6, Anda dapat mencoba {c: cancelPromise, h: httpPromise}
Aliaksandr Hmyrak

Saya paham, object shortinitializer
Mephiztopheles

3

Ini meningkatkan jawaban yang diterima dengan mendekorasi layanan $ http dengan metode batalkan sebagai berikut ...

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

APA YANG DILAKUKAN KODE INI?

Untuk membatalkan permintaan, batas waktu "janji" harus ditetapkan. Jika tidak ada batas waktu yang ditetapkan pada permintaan HTTP maka kode akan menambahkan batas waktu "janji". (Jika batas waktu sudah ditetapkan maka tidak ada yang berubah).

Namun, untuk menyelesaikan janji itu kita perlu menangani "ditangguhkan". Dengan demikian kami menggunakan peta sehingga kami dapat mengambil "ditangguhkan" nanti. Ketika kita memanggil metode batal, "ditangguhkan" diambil dari peta dan kemudian kita memanggil metode tekad untuk membatalkan permintaan http.

Semoga ini bisa membantu seseorang.

BATASAN

Saat ini ini hanya berfungsi untuk $ http.get tetapi Anda dapat menambahkan kode untuk $ http.post dan sebagainya

CARA PENGGUNAAN ...

Anda kemudian dapat menggunakannya, misalnya, pada perubahan status, sebagai berikut ...

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

Saya membuat aplikasi yang menjalankan beberapa permintaan http pada saat yang sama dan saya perlu secara manual membatalkan semuanya. Saya sudah mencoba kode Anda tetapi hanya membatalkan permintaan terakhir. Apakah itu terjadi pada Anda sebelumnya? Bantuan apa pun akan dihargai.
Miguel Trabajo

1
kode di sini memelihara pencarian referensi ke objek penangguhan sehingga mereka dapat diambil nanti karena objek penangguhan diperlukan untuk melakukan pembatalan. yang penting dengan pencarian adalah kuncinya: pasangan nilai. Nilainya adalah objek penundaan. Kuncinya adalah string yang dihasilkan berdasarkan metode / url permintaan. Saya menduga Anda membatalkan beberapa permintaan dengan metode / url yang sama. Karena ini semua kunci identik dan mereka menimpa satu sama lain di peta. Anda perlu mengubah logika pembuatan kunci sehingga yang unik dihasilkan walaupun url / metodenya sama.
danday74

1
lanjutan dari atas ... ini bukan bug dalam kode, kode menangani aborsi beberapa permintaan ... tetapi kode itu tidak pernah dimaksudkan untuk berurusan dengan membatalkan beberapa permintaan ke url yang sama menggunakan metode http yang sama ... tetapi jika Anda mengubah logika Anda harus bisa membuatnya bekerja dengan cukup mudah.
danday74

1
Terima kasih banyak! Saya membuat beberapa permintaan ke url yang sama tetapi dengan parameter yang berbeda, dan setelah Anda mengatakan tentang hal itu saya mengubah baris itu dan itu berfungsi seperti pesona!
Miguel Trabajo

1

di sini adalah versi yang menangani beberapa permintaan, juga memeriksa status yang dibatalkan dalam panggilan balik untuk menekan kesalahan dalam blok kesalahan. (dalam naskah)

tingkat pengontrol:

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

di http saya dapatkan:

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

metode pembantu

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

sekarang melihat pada tab jaringan, saya melihat itu bekerja dengan sangat baik. Saya memanggil metode ini 4 kali dan hanya yang terakhir saja yang lewat.

masukkan deskripsi gambar di sini


req.resolve ('dibatalkan'); tidak berfungsi untuk saya, saya menggunakan versi 1.7.2. Bahkan saya ingin membatalkan panggilan jika dipanggil lagi dan panggilan pertama masih dalam status tertunda. tolong bantu. saya selalu ingin memberikan data panggilan yang baru dipanggil dengan membatalkan semua api yang menunggu dari url yang sama
Sudarshan Kalebere

1

Anda dapat menambahkan fungsi kustom ke $httplayanan menggunakan "dekorator" yang akan menambahkan abort()fungsi ke janji Anda.

Berikut beberapa kode yang berfungsi:

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

Kode ini menggunakan fungsionalitas dekorator angularjs untuk menambahkan with_abort()fungsi ke $httplayanan.

with_abort()menggunakan $httpopsi batas waktu yang memungkinkan Anda untuk membatalkan permintaan http.

Janji yang dikembalikan dimodifikasi untuk memasukkan abort()fungsi. Ini juga memiliki kode untuk memastikan bahwa abort()pekerjaan bahkan jika Anda berantai menjanjikan.

Berikut ini adalah contoh bagaimana Anda akan menggunakannya:

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

Secara default saat Anda menelepon abort()permintaan dibatalkan dan tidak ada penangan janji berjalan.

Jika Anda ingin penangan kesalahan Anda dipanggil lewat benar abort(true).

Di penangan kesalahan Anda, Anda dapat memeriksa apakah "kesalahan" disebabkan oleh "batalkan" dengan memeriksa xhrStatusproperti. Ini sebuah contoh:

var promise = $http.with_abort({ method: 'GET', url: '/names' }).then(
    function(names) {
        do_something(names));
    }, 
    function(error) {
        if (er.xhrStatus === "abort") return;
    });
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.