Jak anulować / przerwać żądanie AJAX jQuery?

244

Mam żądanie AJAX, które będzie wysyłane co 5 sekund. Ale problem występuje przed żądaniem AJAX, jeśli poprzednie żądanie nie zostało zakończone, muszę przerwać to żądanie i złożyć nowe żądanie.

Mój kod jest mniej więcej taki, jak rozwiązać ten problem?

$(document).ready(
    var fn = function(){
        $.ajax({
            url: 'ajax/progress.ftl',
            success: function(data) {
                //do something
            }
        });
    };

    var interval = setInterval(fn, 500);
);
użytkownik556673
źródło
5
Twoje pytanie mówi, że żądanie powinno się zdarzać co 5 sekund, ale kod używa 500 ms = 0,5 s.
geon

Odpowiedzi:

355

Metoda jquery ajax zwraca obiekt XMLHttpRequest . Możesz użyć tego obiektu, aby anulować żądanie.

XMLHttpRequest ma metodę przerwania , która anuluje żądanie, ale jeśli żądanie zostało już wysłane do serwera, serwer przetworzy żądanie, nawet jeśli przerwiemy żądanie, ale klient nie będzie czekał / obsłużył odpowiedzi.

Obiekt xhr zawiera również stan readyState, który zawiera stan żądania (UNSENT-0, OPENED-1, HEADERS_RECEIVED-2, LOADING-3 i DONE-4). możemy to wykorzystać do sprawdzenia, czy poprzednie żądanie zostało zrealizowane.

$(document).ready(
    var xhr;

    var fn = function(){
        if(xhr && xhr.readyState != 4){
            xhr.abort();
        }
        xhr = $.ajax({
            url: 'ajax/progress.ftl',
            success: function(data) {
                //do something
            }
        });
    };

    var interval = setInterval(fn, 500);
);
Arun P. Johny
źródło
60
@romkyns Różnice są właściwością readystatepowyżej i readyStateponiżej, postać sjest pisana wielkimi literami w jQuery 1.5
Arun P Johny
Zastanawiałem się, co się stanie, jeśli readyState to „LOADING-3”, a my przerwiemy? Dane są wysyłane do serwera i są przetwarzane lub nie. Nie będziemy wiedzieć ...: S
inf3rno,
7
Przy okazji, myślę, że kontrola readyState jest niepotrzebna, ponieważ jest odroczona, a gdy zostanie rozwiązana lub odrzucona, nie uruchomi pozostałych. Więc też if (xhr) xhr.abort();będzie dobrze działać.
Ciantic
2
W3 nigdy nie używał wielkich liter readystatew swojej dokumentacji. Zawsze była pisana wielkimi literami, readyStatea jquery (przed 1.5 lub później) poprawnie pasowało do specyfikacji w3.
Arashsoft,
@ArunPJohny, czy możesz zadać to pytanie stackoverflow.com/questions/49801146/…
Krishna Jonnalagadda
6

Kiedy wysyłasz żądanie do serwera, sprawdź, czy najpierw postęp nie jest zerowy (lub nie pobiera tych danych). Jeśli pobiera dane, przerwij poprzednie żądanie i zainicjuj nowe.

var progress = null

function fn () {    
    if (progress) {
        progress.abort();
    }
    progress = $.ajax('ajax/progress.ftl', {
        success: function(data) {
            //do something
            progress = null;
        }
    });
}
Taz
źródło
5

Wiem, że to może być trochę za późno, ale mam podobne problemy, gdy wywołanie metody przerwania naprawdę nie przerwało żądania. zamiast tego przeglądarka wciąż czekała na odpowiedź, której nigdy nie używa. ten kod rozwiązał ten problem.

 try {
        xhr.onreadystatechange = null;
        xhr.abort();
} catch (e) {}
Lyon
źródło
1

Dlaczego warto przerwać prośbę?

Jeśli każde żądanie zajmie więcej niż pięć sekund, co się stanie?

Nie należy przerywać żądania, jeśli parametr przekazywany z żądaniem nie zmienia się. np .: - żądanie dotyczy odzyskania danych powiadomienia. W takich sytuacjach Przyjemne podejście polega na tym, że ustawia nowe żądanie dopiero po wypełnieniu poprzedniego żądania Ajax.

$(document).ready(

    var fn = function(){

        $.ajax({
            url: 'ajax/progress.ftl',
            success: function(data) {
                //do something
            },

            complete: function(){setTimeout(fn, 500);}
        });
    };

     var interval = setTimeout(fn, 500);

);
Habeeb Perwad
źródło
5
To jest zły pomysł. Podczas rozpoczynania nowego połączenia nie chcesz, aby całe zdarzenie było uruchamiane z poprzedniego połączenia. Przy takim podejściu możesz uzyskać bardzo dziwne wyniki.
Webberig,
1

Możesz użyć jquery-validate.js. Poniżej znajduje się fragment kodu z pliku jquery-validate.js.

// ajax mode: abort
// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()

var pendingRequests = {},
    ajax;
// Use a prefilter if available (1.5+)
if ( $.ajaxPrefilter ) {
    $.ajaxPrefilter(function( settings, _, xhr ) {
        var port = settings.port;
        if ( settings.mode === "abort" ) {
            if ( pendingRequests[port] ) {
                pendingRequests[port].abort();
            }
            pendingRequests[port] = xhr;
        }
    });
} else {
    // Proxy ajax
    ajax = $.ajax;
    $.ajax = function( settings ) {
        var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
            port = ( "port" in settings ? settings : $.ajaxSettings ).port;
        if ( mode === "abort" ) {
            if ( pendingRequests[port] ) {
                pendingRequests[port].abort();
            }
            pendingRequests[port] = ajax.apply(this, arguments);
            return pendingRequests[port];
        }
        return ajax.apply(this, arguments);
    };
}

Musisz tylko ustawić tryb parametrów , aby przerwał się, gdy tworzysz żądanie ajax.

Ref: https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.14.0/jquery.validate.js

zawhtut
źródło
0

jQuery: Użyj tego jako punktu wyjścia - jako inspiracji. Rozwiązałem to w ten sposób: (to nie jest idealne rozwiązanie, po prostu przerywa ostatnią instancję i jest kodem WIP)

var singleAjax = function singleAjax_constructor(url, params) {
    // remember last jQuery's get request
    if (this.lastInstance) {
        this.lastInstance.abort();  // triggers .always() and .fail()
        this.lastInstance = false;
    }

    // how to use Deferred : http://api.jquery.com/category/deferred-object/
    var $def = new $.Deferred();

    // pass the deferrer's request handlers into the get response handlers
    this.lastInstance = $.get(url, params)
        .fail($def.reject)         // triggers .always() and .fail()
        .success($def.resolve);    // triggers .always() and .done()

    // return the deferrer's "control object", the promise object
    return $def.promise();
}


// initiate first call
singleAjax('/ajax.php', {a: 1, b: 2})
    .always(function(a,b,c) {console && console.log(a,b,c);});

// second call kills first one
singleAjax('/ajax.php', {a: 1, b: 2})
    .always(function(a,b,c) {console && console.log(a,b,c);});
    // here you might use .always() .fail() .success() etc.
BananaAcid
źródło
0

Utwórz funkcję, aby wywołać interfejs API. W ramach tej funkcji definiujemy żądanie callApiRequest = $.get(...- chociaż jest to definicja zmiennej, żądanie jest wywoływane natychmiast, ale teraz mamy zdefiniowane żądanie jako zmienną. Przed wywołaniem żądania sprawdzamy, czy nasza zmienna jest zdefiniowana, typeof(callApiRequest) != 'undefined'a także czy jest w toku suggestCategoryRequest.state() == 'pending'- jeśli oba są prawdziwe, to .abort()żądanie, które zapobiegnie uruchomieniu wywołania zwrotnego powodzenia.

// We need to wrap the call in a function
callApi = function () {

    //check if request is defined, and status pending
    if (typeof(callApiRequest) != 'undefined'
        && suggestCategoryRequest.state() == 'pending') {

        //abort request
        callApiRequest.abort()

    }

    //define and make request
    callApiRequest = $.get("https://example.com", function (data) {

        data = JSON.parse(data); //optional (for JSON data format)
        //success callback

    });
}

Twój serwer / API może nie obsługiwać przerywania żądania (co jeśli API wykonał już jakiś kod?), Ale wywołanie zwrotne javascript nie zostanie uruchomione. Jest to przydatne, gdy na przykład podajesz sugestie wejściowe użytkownikowi, takie jak dane hashtags.

Możesz dodatkowo rozszerzyć tę funkcję, dodając definicję wywołania zwrotnego błędu - co powinno się stać, jeśli żądanie zostanie przerwane.

Typowym przypadkiem użycia tego fragmentu kodu byłoby wprowadzanie tekstu, który jest uruchamiany w keypressprzypadku zdarzenia. Możesz użyć limitu czasu, aby zapobiec wysyłaniu (niektórych) żądań, które będziesz musiał anulować .abort().

mate.gwozdz
źródło
-7

Powinieneś również sprawdzić dla readyState 0. Ponieważ kiedy używasz xhr.abort () ta funkcja ustawia readyState na 0 w tym obiekcie, a twoje sprawdzenie jeśli zawsze będzie prawdziwe - readyState! = 4

$(document).ready(
    var xhr;

    var fn = function(){
        if(xhr && xhr.readyState != 4 && xhr.readyState != 0){
            xhr.abort();
        }
        xhr = $.ajax({
            url: 'ajax/progress.ftl',
            success: function(data) {
                //do something
            }
        });
    };

    var interval = setInterval(fn, 500);
); 
programista
źródło
2
Czy skopiowałeś i wkleiłeś odpowiedź Aruna? Ponieważ prawdopodobieństwo, że dwa bloki kodu są dokładnie takie same, odstępy i wszystkie ...
@ user2700923 Jego post nie jest dokładnie taki sam. Chodzi o to, że powinniśmy również sprawdzić stan gotowości 0. Jednak byłoby to bardziej odpowiednie jako komentarz do odpowiedzi Aruna.
Stefan