AngularJS में $ http अनुरोध रद्द कैसे करें?


190

AngularJS में अजाक्स अनुरोध को देखते हुए

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

उस अनुरोध को रद्द करने का सबसे प्रभावी तरीका क्या है यदि कोई अन्य अनुरोध लॉन्च किया गया है (उदाहरण के लिए एक ही बैकेंड, विभिन्न मापदंडों)।


8
नीचे दिए गए उत्तरों में से कोई भी वास्तव में अनुरोध को रद्द नहीं करता है। एक बार ब्राउज़र छोड़ने के बाद HTTP अनुरोध को रद्द करने का कोई तरीका नहीं है। नीचे दिए गए सभी उत्तर किसी तरह से श्रोता को छोड़ देते हैं। HTTP अनुरोध अभी भी सर्वर को हिट करता है, अभी भी संसाधित है और सर्वर अभी भी एक प्रतिक्रिया भेजेगा, यह सिर्फ एक मामला है कि ग्राहक अभी भी उस प्रतिक्रिया के लिए लिसिंग कर रहा है या नहीं।
लियाम

मार्ग परिवर्तन पर $ http अनुरोध रद्द करें freakyjolly.com/how-to-cancel-http-requests-in-angularjs-app
कोड जासूस


@Liam मेरा सवाल सर्वर पर रद्द नहीं हो रहा था। यह बहुत विशिष्ट होगा कि आपकी सर्वर तकनीक / कार्यान्वयन क्या है। मुझे कॉलबैक छोड़ने का संबंध था
सोनिक सोल

जवाबों:


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.

13
यदि मुझे वादे के माध्यम से टाइमआउट और मैनुअल रद्द करने की आवश्यकता है तो मुझे क्या करना चाहिए?
रमन चोदका

15
@ रमनचोड़का आप दोनों एक वचन के साथ कर सकते हैं; आप जावास्क्रिप्ट के मूल setTimeoutकार्य या एंगुलर की $timeoutसेवा के साथ, कुछ समय के बाद वादे को रद्द करने के लिए एक समय सीमा निर्धारित कर सकते हैं ।
क्विन स्ट्राल

9
रद्द करें। भावी () भविष्य के अनुरोधों को रद्द कर देगा। यह एक बेहतर उपाय है: odetocode.com/blogs/scott/archive/2014/04/24/…
टूलकिट

7
बेन नादेल
Pete

3
वास्तव में काम नहीं करता है। क्या आप एक कार्य नमूना प्रदान कर सकते हैं?
एडवर्ड ओलिमिसन

10

समयबाह्य संपत्ति के साथ कोणीय $ http अजाक्स रद्द करना कोणीय 1.3.15 में काम नहीं करता है। उन लोगों के लिए जो इस बात का इंतजार नहीं कर सकते हैं कि मैं एक jQuery Ajax समाधान कोणीय में लिपटे साझा कर रहा हूं।

समाधान में दो सेवाएं शामिल हैं:

  • HttpService (jQuery Ajax फ़ंक्शन के आसपास एक आवरण);
  • PendingRequestsService (लंबित / खुले अजाक्स अनुरोधों को ट्रैक करता है)

यहाँ 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

जारी किए गए अनुरोधों $httpको रद्द करना AngularJS के वर्तमान संस्करण के साथ समर्थित नहीं है। इस क्षमता को जोड़ने के लिए एक पुल अनुरोध खोला गया है, लेकिन इस पीआर की अभी तक समीक्षा नहीं की गई है, इसलिए यह स्पष्ट नहीं है कि क्या इसे एंगुलरजेज कोर में बनाया जाएगा।


उस पीआर को अस्वीकार कर दिया गया था, ओपी ने यहाँ एक अद्यतन प्रस्तुत किया github.com/angular/angular.js/pull/1836
मार्क नादिग

और वह भी बंद था।
frapontillo

इसका एक संस्करण इस रूप में उतरा । फिर भी अंतिम संस्करण का उपयोग करने के लिए सिंटैक्स का पता लगाने की कोशिश कर रहा है। काश पीआर उपयोग नमूने के साथ आया! :)
सिम्पलीगैस

'उपयोग' में कोणीय प्रलेखन पृष्ठ docs.angularjs.org/api/ng/service/$http एक टाइमआउट सेटिंग का वर्णन करता है, और यह भी उल्लेख करता है कि किन वस्तुओं (एक वादा) को स्वीकार किया जाता है।
इगोर लिनो

6

यदि आप ui- राउटर के साथ 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 config में

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

यह मेरे लिए काम करता है - बहुत सरल और मैंने कॉल को नाम देने के लिए एक और जोड़ा ताकि मैं कॉल का चयन कर
सकूं

यूआई राउटर कॉन्फ़िगरेशन request.timeoutको वर्तमान में जानने की आवश्यकता क्यों है ?
कोशिशें

6

किसी कारण के लिए config.timeout मेरे लिए काम नहीं करता है। मैंने इस दृष्टिकोण का उपयोग किया:

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

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

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

और CancelRequest.resolve () रद्द करने के लिए। वास्तव में यह एक अनुरोध को रद्द नहीं करता है, लेकिन आपको कम से कम अनावश्यक प्रतिक्रिया नहीं मिलती है।

उम्मीद है की यह मदद करेगा।


क्या आपने अपना सिंटेक्स एरर देखा { cancelPromise, httpPromise }?
मेफिज़ोटोफल्स

यह ES6 सिंटैक्स है, आप {c: CancelPromise, h: httpPromise}
अलीकांदर ह्यमरक

मैं देखता हूं, ऑब्जेक्ट शॉर्टिनिटाइज़र
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 अनुरोध पर कोई टाइमआउट सेट नहीं है, तो कोड एक "वादा" टाइमआउट जोड़ता है। (यदि कोई टाइमआउट पहले से सेट है तो कुछ भी नहीं बदला जाता है)।

हालांकि, वादे को हल करने के लिए हमें "स्थगित" पर एक हैंडल की आवश्यकता है। इस प्रकार हम एक मानचित्र का उपयोग करते हैं ताकि हम बाद में "स्थगित" को पुनः प्राप्त कर सकें। जब हम गर्भपात विधि को कहते हैं, तो "स्थगित" को मानचित्र से पुनर्प्राप्त किया जाता है और फिर हम http अनुरोध को रद्द करने के लिए संकल्प विधि को कॉल करते हैं।

आशा है कि यह किसी की मदद करता है।

सीमाएँ

वर्तमान में यह केवल $ http.get के लिए काम करता है लेकिन आप $ http.post इत्यादि के लिए कोड जोड़ सकते हैं

कैसे इस्तेमाल करे ...

आप तब इसका उपयोग कर सकते हैं, उदाहरण के लिए, राज्य परिवर्तन पर, निम्नानुसार ...

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

मैं एक ऐसा ऐप बना रहा हूं जो एक ही समय में कुछ http अनुरोधों को निकालता है और मुझे उन सभी को मैन्युअल रूप से निरस्त करने की आवश्यकता है। मैंने आपका कोड आज़मा लिया है, लेकिन यह केवल अंतिम अनुरोध को रद्द करता है। क्या आपके साथ पहले भी ऐसा हुआ था? किसी भी सहायता की सराहना की जाएगी।
मिगुएल ट्रेजाजो

1
यहाँ कोड defer ऑब्जेक्ट्स के संदर्भों की खोज को बनाए रखता है ताकि बाद में उन्हें पुनः प्राप्त किया जा सके क्योंकि defer ऑब्जेक्ट को abort करना आवश्यक है। लुकअप के साथ महत्वपूर्ण चीज है: वैल्यू पेयर। मान defer ऑब्जेक्ट है। कुंजी एक स्ट्रिंग है जो अनुरोध विधि / url के आधार पर उत्पन्न होती है। मैं अनुमान लगा रहा हूं कि आप एक ही विधि / url में कई अनुरोधों को निरस्त कर रहे हैं। इस वजह से सभी कुंजी समान हैं और वे मानचित्र में एक दूसरे को ओवरराइट करते हैं। आपको मुख्य पीढ़ी के तर्क को मोड़ने की आवश्यकता है ताकि एक अद्वितीय उत्पन्न हो, भले ही url / पद्धति समान हो।
danday74

1
ऊपर से जारी ... यह कोड में बग नहीं है, कोड कई अनुरोधों को निरस्त करने का काम करता है ... लेकिन कोड का मतलब कभी भी एक ही http विधि का उपयोग करके एक ही यूआरएल में कई अनुरोधों को निरस्त करने से नहीं था ... लेकिन यदि आप तर्क को घुमाते हैं तो आपको इसे आसानी से काम करने में सक्षम होना चाहिए।
danday74

1
आपका बहुत बहुत धन्यवाद! मैं एक ही url पर विभिन्न मापदंडों के साथ कई अनुरोध कर रहा था, और आपके द्वारा इस बारे में कहने के बाद कि मैंने उस लाइन को बदल दिया है और यह एक आकर्षण की तरह काम करता है!
मिगेल ट्रेजाजो

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 के सभी लंबित एपीआई को रद्द करके नव कॉल कॉल डेटा प्रदान करना चाहता
हूं

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

यह कोड सेवा में 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()करते हैं तो अनुरोध रद्द हो जाता है और कोई भी वादे चलाने वाले नहीं होते हैं।

यदि आप चाहते हैं कि आपका त्रुटि हैंडलर पास को सच कहा जाए 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;
    });
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.