Làm cách nào để hủy yêu cầu $ http trong AngularJS?


189

Đưa ra yêu cầu Ajax trong AngularJS

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

cách hiệu quả nhất để hủy yêu cầu đó nếu một yêu cầu khác được đưa ra (ví dụ như phụ trợ, các tham số khác nhau).


8
Không có câu trả lời nào dưới đây thực sự hủy bỏ yêu cầu. Không có cách nào để hủy yêu cầu HTTP một khi nó rời khỏi trình duyệt. Tất cả các câu trả lời dưới đây chỉ đơn giản là abondon người nghe theo một cách nào đó. Yêu cầu HTTP vẫn truy cập vào máy chủ, vẫn được xử lý và máy chủ vẫn sẽ gửi phản hồi, đó chỉ là trường hợp khách hàng vẫn còn lờ mờ cho phản hồi đó hay không.
Liam

$ http yêu cầu hủy trên tuyến đường thay đổi freakyjolly.com/how-to-celon-http-requests-in-angularjs-app
Code Spy


@Liam câu hỏi của tôi không bị hủy trên máy chủ. điều đó sẽ rất cụ thể đối với công nghệ / triển khai máy chủ của bạn. tôi đã quan tâm đến việc từ bỏ cuộc gọi lại
Sonic Soul

Câu trả lời:


325

Tính năng này đã được thêm vào bản phát hành 1.1.5 thông qua thời gian chờ:

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

13
Tôi nên làm gì trong trường hợp tôi cần cả thời gian chờ và hủy thủ công qua lời hứa?
Raman Jigźka

15
@ RamanChodźka Bạn có thể thực hiện cả hai với một lời hứa; bạn có thể đặt thời gian chờ để hủy lời hứa sau một khoảng thời gian, với setTimeoutchức năng gốc của JavaScript hoặc $timeoutdịch vụ của Angular .
Quinn Strahl

9
Cancer.resolve () sẽ hủy các yêu cầu trong tương lai. Đây là một giải pháp tốt hơn: odetocode.com/bloss/scott/archive/2014/04/24/ trên
Bộ công cụ

7
một ví dụ điển hình khác về một giải pháp hoàn chỉnh hơn từ Ben Nadel: bennadel.com/blog/ Kẻ
Pete

3
Không thực sự làm việc. Bạn có thể cung cấp một mẫu làm việc?
Edward Olamisan

10

Hủy bỏ Angular $ http Ajax với thuộc tính hết thời gian không hoạt động trong Angular 1.3.15. Đối với những người không thể chờ đợi để sửa lỗi này, tôi đang chia sẻ giải pháp jQuery Ajax được gói trong Angular.

Giải pháp liên quan đến hai dịch vụ:

  • Dịch vụ httpService (trình bao bọc xung quanh hàm jQuery Ajax);
  • PendingRequestsService (theo dõi các yêu cầu Ajax đang chờ / mở)

Ở đây có dịch vụ 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);

Dịch vụ 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);

Sau này trong dịch vụ của bạn khi bạn đang tải dữ liệu, bạn sẽ sử dụng Dịch vụ httpS thay vì $ http:

(function (angular) {

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

        this.getResources = function (params) {

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

        };
    }]);

})(window.angular);

Sau đó trong mã của bạn, bạn muốn tải dữ liệu:

(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

Hủy bỏ các yêu cầu được ban hành $httpkhông được hỗ trợ với phiên bản hiện tại của AngularJS. Có một yêu cầu kéo được mở để thêm khả năng này nhưng PR này chưa được xem xét nên chưa rõ liệu nó có biến nó thành lõi AngularJS hay không.


rằng PR đã bị từ chối, OP đã gửi cập nhật tại đây github.com/angular/angular.js/pull/1836
Mark Nadig

Và điều đó cũng đã bị đóng cửa.
frapontillo

Một phiên bản của nó hạ cánh như thế này . Vẫn đang cố gắng tìm ra cú pháp để sử dụng phiên bản cuối cùng. Chúc các PR đến với các mẫu sử dụng! :)
SimplGy

Trang tài liệu góc docs.angularjs.org/api/ng/service/$http trong 'Cách sử dụng' mô tả cài đặt thời gian chờ và cũng đề cập đến những đối tượng (Lời hứa) được chấp nhận.
Igor Lino

6

Nếu bạn muốn hủy các yêu cầu đang chờ xử lý trên stateChangeStart bằng ui-router, bạn có thể sử dụng một cái gì đó như thế này:

// phục vụ

                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;

// trong cấu hình 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();
          }
       });
    });

Điều này hiệu quả với tôi - rất đơn giản và tôi đã thêm một cuộc gọi khác để đặt tên cho cuộc gọi để tôi có thể chọn cuộc gọi và chỉ hủy một số cuộc gọi
Simon Dragsbæk

Tại sao cấu hình UI Router cần biết nếu request.timeoutcó?
phân tích

6

Vì một số lý do config.timeout không hoạt động với tôi. Tôi đã sử dụng phương pháp này:

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

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

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

Và hủyRequest.resolve () để hủy. Trên thực tế, nó không hủy yêu cầu nhưng ít nhất bạn không nhận được phản hồi không cần thiết.

Hi vọng điêu nay co ich.


Bạn đã thấy SyntaxError của bạn { cancelPromise, httpPromise }?
Mephiztopheles

đây là cú pháp ES6, bạn có thể thử {c: CancPromise, h: httpPromise}
Aliaksandr Hmyrak

Tôi thấy, đối tượng shortinitializer
Mephiztopheles

3

Điều này giúp tăng cường câu trả lời được chấp nhận bằng cách trang trí dịch vụ $ http bằng phương pháp hủy bỏ như sau ...

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

MÃ NÀY LÀM GÌ?

Để hủy yêu cầu, thời gian chờ "lời hứa" phải được đặt. Nếu không có thời gian chờ nào được đặt trên yêu cầu HTTP thì mã sẽ thêm thời gian chờ "lời hứa". (Nếu thời gian chờ đã được đặt thì không có gì thay đổi).

Tuy nhiên, để giải quyết lời hứa, chúng tôi cần xử lý "hoãn lại". Do đó, chúng tôi sử dụng bản đồ để có thể truy xuất "hoãn lại" sau. Khi chúng tôi gọi phương thức hủy bỏ, "hoãn lại" được lấy từ bản đồ và sau đó chúng tôi gọi phương thức giải quyết để hủy yêu cầu http.

Hy vọng điều này sẽ giúp được ai đó.

GIỚI HẠN

Hiện tại, điều này chỉ hoạt động với $ http.get nhưng bạn có thể thêm mã cho $ http.post, v.v.

CÁCH SỬ DỤNG ...

Sau đó, bạn có thể sử dụng nó, ví dụ, về thay đổi trạng thái, như sau ...

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

Tôi đang tạo một ứng dụng kích hoạt một số yêu cầu http cùng một lúc và tôi cần hủy bỏ tất cả các yêu cầu http. Tôi đã thử mã của bạn nhưng nó chỉ hủy bỏ yêu cầu cuối cùng. Điều đó đã xảy ra với bạn trước đây? Bất kỳ trợ giúp sẽ được đánh giá cao.
Miguel Trabajo

1
mã ở đây duy trì việc tra cứu các tham chiếu đến các đối tượng trì hoãn để chúng có thể được truy xuất sau đó vì đối tượng trì hoãn được yêu cầu hủy bỏ. điều quan trọng với việc tra cứu là cặp khóa: value. Giá trị là đối tượng trì hoãn. Khóa này là một chuỗi được tạo dựa trên phương thức / url yêu cầu. Tôi đoán rằng bạn đang hủy bỏ nhiều yêu cầu cho cùng một phương thức / url. Do đó, tất cả các khóa đều giống hệt nhau và chúng ghi đè lên nhau trên bản đồ. Bạn cần điều chỉnh logic tạo khóa để tạo ra một cái duy nhất ngay cả khi url / phương thức giống nhau.
danday74

1
tiếp tục từ phía trên ... đây không phải là lỗi trong mã, mã xử lý hủy bỏ nhiều yêu cầu ... nhưng mã đơn giản là không bao giờ có nghĩa là xử lý hủy bỏ nhiều yêu cầu vào cùng một url bằng cùng một phương thức http ... nhưng nếu bạn tinh chỉnh logic, bạn sẽ có thể làm cho nó hoạt động khá dễ dàng.
danday74

1
Cảm ơn rât nhiều! Tôi đã thực hiện nhiều yêu cầu cho cùng một url nhưng với các tham số khác nhau và sau khi bạn nói về điều đó tôi đã thay đổi dòng đó và nó hoạt động như một cơ duyên!
Miguel Trabajo

1

đây là phiên bản xử lý nhiều yêu cầu, đồng thời kiểm tra trạng thái bị hủy trong cuộc gọi lại để loại bỏ lỗi trong khối lỗi. (trong Bản đánh máy)

cấp điều khiển:

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

trong http của tôi nhận được:

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

phương pháp trợ giúp

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

Bây giờ nhìn vào tab mạng, tôi thấy rằng nó hoạt động rất tốt. tôi đã gọi phương thức này 4 lần và chỉ có lần cuối cùng đi qua.

nhập mô tả hình ảnh ở đây


req.resolve ('đã hủy'); không hoạt động với tôi, tôi đang sử dụng phiên bản 1.7.2. Ngay cả tôi cũng muốn hủy cuộc gọi nếu nó được gọi lại và cuộc gọi đầu tiên vẫn ở trạng thái chờ xử lý. xin vui lòng giúp đỡ. tôi luôn muốn cung cấp dữ liệu cuộc gọi mới được gọi bằng cách hủy tất cả các api đang chờ xử lý của cùng một url
Sudarshan Kalebere

1

Bạn có thể thêm chức năng tùy chỉnh vào $httpdịch vụ bằng cách sử dụng "trình trang trí" sẽ thêm abort()chức năng vào lời hứa của bạn.

Đây là một số mã làm việc:

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

Mã này sử dụng chức năng trang trí của angularjs để thêm with_abort()chức năng cho $httpdịch vụ.

with_abort()sử dụng $httptùy chọn thời gian chờ cho phép bạn hủy bỏ yêu cầu http.

Lời hứa trả lại được sửa đổi để bao gồm một abort()chức năng. Nó cũng có mã để đảm bảo rằng các abort()công trình ngay cả khi bạn hứa hẹn chuỗi.

Đây là một ví dụ về cách bạn sẽ sử dụng nó:

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

Theo mặc định khi bạn gọi abort()yêu cầu bị hủy và không có trình xử lý lời hứa nào chạy.

Nếu bạn muốn xử lý lỗi của bạn được gọi là pass true abort(true).

Trong trình xử lý lỗi của bạn, bạn có thể kiểm tra xem "lỗi" có phải là do "hủy bỏ" hay không bằng cách kiểm tra thuộc xhrStatustính. Đây là một ví dụ:

var promise = $http.with_abort({ method: 'GET', url: '/names' }).then(
    function(names) {
        do_something(names));
    }, 
    function(error) {
        if (er.xhrStatus === "abort") return;
    });
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.