Как отменить / отменить JQuery AJAX-запрос?


244

У меня есть запрос AJAX, который будет выполняться каждые 5 секунд. Но проблема в том, что перед запросом AJAX, если предыдущий запрос не выполнен, я должен прервать этот запрос и сделать новый запрос.

Мой код примерно такой, как решить эту проблему?

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

    var interval = setInterval(fn, 500);
);


5
В вашем вопросе говорится, что запрос должен выполняться каждые 5 секунд, но код использует 500 мс = 0,5 с.
Geon

Ответы:


355

Ajax-метод jquery возвращает объект XMLHttpRequest . Вы можете использовать этот объект для отмены запроса.

XMLHttpRequest имеет метод abort , который отменяет запрос, но если запрос уже был отправлен на сервер, то сервер обработает запрос, даже если мы прервем запрос, но клиент не будет ждать / обрабатывать ответ.

Объект xhr также содержит readyState, который содержит состояние запроса (UNSENT-0, OPENED-1, HEADERS_RECEIVED-2, LOADING-3 и DONE-4). мы можем использовать это, чтобы проверить, был ли выполнен предыдущий запрос.

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

60
@romkyns Различия - это свойство readystateвыше и readyStateниже, символ sзаглавной буквы в jQuery 1.5
Арун П Джонни

Мне было интересно, что произойдет, если readyState будет «ЗАГРУЗКА-3» и мы прервемся? Данные отправляются на сервер, и обрабатываются они или нет. Мы не будем знать ...: S
inf3rno

7
Кстати, я думаю, что проверка readyState не нужна, поскольку она откладывается, и, как только она решена или отклонена, она не запустит другие. Так что if (xhr) xhr.abort();будет работать нормально.
Ciantic

2
W3 никогда не использовал заглавные буквы readystateв своей документации. Он всегда был написан заглавными буквами, readyStateи jquery (до или после 1.5) правильно соответствовал спецификации w3.
Arashsoft

@ArunPJohny, не могли бы вы задать этот вопрос stackoverflow.com/questions/49801146/…
Кришна Джонналагадда

6

Когда вы отправляете запрос на сервер, сначала проверьте, не является ли прогресс первым (или извлекает эти данные). Если он выбирает данные, отмените предыдущий запрос и инициируйте новый.

var progress = null

function fn () {    
    if (progress) {
        progress.abort();
    }
    progress = $.ajax('ajax/progress.ftl', {
        success: function(data) {
            //do something
            progress = null;
        }
    });
}

5

Я знаю, что это может быть немного поздно, но у меня возникают похожие проблемы, когда вызов метода abort действительно не отменяет запрос. вместо этого браузер все еще ждал ответа, который он никогда не использует. этот код решил эту проблему.

 try {
        xhr.onreadystatechange = null;
        xhr.abort();
} catch (e) {}

1

Почему вы должны прервать запрос?

Если каждый запрос занимает более пяти секунд, что произойдет?

Вы не должны прерывать запрос, если параметр, передаваемый вместе с запросом, не изменяется. Например: - запрос на получение данных уведомления. В таких ситуациях хороший подход заключается в том, что новый запрос устанавливается только после завершения предыдущего 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);

);

5
Это плохая идея. При запуске нового вызова вы не хотите, чтобы завершенное событие было запущено по сравнению с предыдущим вызовом. При таком подходе вы можете получить очень странные результаты.
Веббериг

1

Вы можете использовать jquery-validate.js. Ниже приведен фрагмент кода из 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);
    };
}

Так что вам просто нужно установить режим параметров, чтобы прервать, когда вы делаете ajax-запрос.

Ссылка: https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.14.0/jquery.validate.js


0

jQuery: используйте это как отправную точку - как вдохновение. Я решил это так: (это не идеальное решение, оно просто прерывает последнюю инстанцию ​​и использует код 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.

0

Создайте функцию для вызова вашего API. В этой функции мы определяем запрос callApiRequest = $.get(...- даже если это определение переменной, запрос вызывается немедленно, но теперь у нас есть запрос, определенный как переменная. Перед вызовом запроса мы проверяем, определена ли наша переменная, typeof(callApiRequest) != 'undefined'а также, ожидает ли она ответа suggestCategoryRequest.state() == 'pending'- если оба значения верны, мы .abort()выполняем запрос, который не позволит выполнить успешный обратный вызов.

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

    });
}

Ваш сервер / API может не поддерживать прерывание запроса (что, если API уже выполнил некоторый код?), Но обратный вызов javascript не сработает. Это полезно, когда, например, вы предоставляете пользователю предложения ввода, такие как ввод хэштегов.

Вы можете дополнительно расширить эту функцию, добавив определение обратного вызова ошибки - что должно произойти, если запрос был прерван.

Распространенным вариантом использования этого фрагмента будет ввод текста, который инициирует keypressсобытие. Вы можете использовать тайм-аут, чтобы предотвратить отправку (некоторых) запросов, которые вам придется отменить .abort().


-7

Вы также должны проверить для readyState 0. Потому что когда вы используете xhr.abort (), эта функция устанавливает readyState в 0 в этом объекте, и ваша проверка if всегда будет true - 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);
); 

2
Вы скопировали и вставили ответ Аруна? Потому что вероятность того, что два кодовых блока будут одинаковыми, с одинаковым интервалом и всем ...

@ user2700923 Его пост не совсем такой же. Суть его в том, что мы должны также проверить готовность к состоянию 0. Однако это было бы более уместно в качестве комментария к ответу Аруна.
Стефан
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.