jQuery ajax перед отправкой

У меня есть простой вызов AJAX, который выполняет функцию на beforeSend и на complete. Они выполняются нормально, но beforeSend "по-видимому" не выполняется до тех пор, пока не будет достигнут успех.

На beforeSend есть уведомление "Подождите". Если я поставлю перерыв после функции в beforeSend, тогда она покажет это уведомление, а затем добьется успеха. Без точки останова он будет сидеть и думать, ожидая ответа, а затем мое уведомление, пожалуйста, подождите, появится на долю секунды после успеха.

Желаемая функциональность состоит в том, чтобы уведомление появлялось сразу после отправки запроса, чтобы оно отображалось во время ожидания ответа.

        $.ajax({
            type : 'POST',
            url : url,
            async : false,
            data : postData,
            beforeSend : function (){
                $.blockUI({
                    fadeIn : 0,
                    fadeOut : 0,
                    showOverlay : false
                });
            },
            success : function (returnData) {
                //stuff
            },
            error : function (xhr, textStatus, errorThrown) {
                //other stuff
            },
            complete : function (){
                $.unblockUI();
            }
        });

person user1134179    schedule 16.04.2013    source источник
comment
Примечание: по какой причине async=false?   -  person Dom    schedule 17.04.2013
comment
Он обновляет часто используемый пользовательский интерфейс на странице. Я не хочу, чтобы пользователи могли использовать эти действия во время его выполнения.   -  person user1134179    schedule 17.04.2013
comment
beforeSend не имеет смысла в сочетании с async:false. Если вы хотите сохранить async:false, вы можете просто добавить вызов blockUI перед ajax(...), так как код все равно будет выполняться синхронно.   -  person Steve    schedule 17.04.2013
comment
Я удалил beforeSend и поместил blockUI перед запросом, однако функциональность не изменилась.   -  person user1134179    schedule 17.04.2013
comment
Что вы имеете в виду под функционалом, который не изменился? Вы говорите, что вызов blockUI все еще выполняется после обработчика success?   -  person Steve    schedule 17.04.2013
comment
Он не изменился по сравнению с тем, что я описал выше. Он вызывается перед обработчиком успеха. Однако он не отображает blockUI, пока не нажмет функцию успеха.   -  person user1134179    schedule 17.04.2013


Ответы (4)


Ваша проблема - флаг async:false. Помимо того факта, что это плохая практика (и действительно имеет смысл только в очень ограниченном числе случаев), она на самом деле нарушает порядок выполнения остального кода. Вот почему:

Кажется, что где-то в коде blockUI они устанавливают setTimeout. В результате код blockUI ожидает очень короткое время. Поскольку следующей инструкцией в очереди является вызов ajax(), выполнение blockUI размещается сразу за ним. А так как вы используете async:false, он должен дождаться завершения полного вызова ajax, прежде чем его можно будет запустить.

Подробно вот что происходит:

  • Вы звоните blockUI
  • blockUI имеет setTimeout и запускается после истечения тайм-аута (даже если длина тайм-аута равна 0, следующая строка, ajax(), будет запущена первой)
  • ajax() вызывается с async:false, что означает, что JS останавливает все, пока запрос не вернется
  • ajax() возвращается успешно, и выполнение JS может быть продолжено
  • код setTimeout внутри blockUI, вероятно, закончился, поэтому он будет выполнен следующим
  • похоже, что blockUI выполняется как часть success, но на самом деле он просто поставлен в очередь из-за тайм-аута

Если бы вы НЕ использовали async:false, выполнение было бы следующим:

  • Вы звоните blockUI
  • blockUI имеет setTimeout и запускается после истечения тайм-аута (даже если длина тайм-аута равна 0, следующая строка, ajax(), будет запущена первой)
  • ajax() вызывается и отправляет запрос на сервер.
  • пока он подключается к серверу, нормальное выполнение JS продолжается
  • код setTimeout внутри blockUI, вероятно, закончился, поэтому он будет выполнен следующим
  • появляется текст blockUI
  • если где-то нет больше кода JS, выполнение JS выполняется до тех пор, пока не будут выполнены обратные вызовы AJAX success и complete

Вот несколько примеров jsFiddle, демонстрирующих проблему:

Пример 1: это ситуация, с которой вы столкнулись. Текст blockUI не отображается до тех пор, пока не будет выполнен вызов ajax.

Пример 2: точно такая же ситуация, как у вас, но с alert перед вызовом ajax. Поскольку есть alert, тайм-аут внутри blockUI помещает появление текста blockUI после alert, а не после ajax.

Пример 3: Вот как это должно работать без async:false

person Steve    schedule 16.04.2013

Скорее всего, это из-за async : false. Поскольку ваш вызов является синхронным, после того, как ваш вызов функции $.ajax() начинается, ничего не происходит, пока не будет получен ответ, и следующим шагом вашего кода будет обработчик success.

Чтобы заставить его работать, вы можете сделать что-то вроде этого

$.blockUI({
        fadeIn : 0,
        fadeOut : 0,
        showOverlay : false
});
// and here goes your synchronous ajax call
$.ajax({
            type : 'POST',
            url : url,
            async : false,
            data : postData,
            success : function (returnData) {
                //stuff
            },
            error : function (xhr, textStatus, errorThrown) {
                //other stuff
            },
            complete : function (){
                $.unblockUI();
            }
     });
person Mohammad Adil    schedule 16.04.2013
comment
Я согласен с вашими рассуждениями... и хочу добавить, что технически вам даже не нужен обратный вызов complete. Вы можете просто добавить $.unblockUI() после раздела ajax(), так как это все равно асинхронно. - person Steve; 17.04.2013
comment
Я удалил beforeSend и завершил и поместил логику до и после запроса .ajax, однако функциональность не изменилась. - person user1134179; 17.04.2013

$.blockUI({
        fadeIn : 0,
        fadeOut : 0,
        showOverlay : false
});
setTimeout(function() {
     $.ajax({
            type : 'POST',
            url : url,
            async : false,
            data : postData,
            success : function (returnData) {
                //stuff
            },
            error : function (xhr, textStatus, errorThrown) {
                //other stuff
            }
     });
},100);
$.unblockUI();

http://bugs.jquery.com/ticket/7464

person IcThuX    schedule 13.02.2014

Другой подход может заключаться в перегрузке функции $.ajax.

$.orig_ajax = $.ajax;

$.ajax = function() {
    var settings = {async: true};
    if (2 == arguments.length && 'string' == typeof arguments[0] && 'object' == typeof arguments[1])
        settings = arguments[1];
    else if (arguments.length && 'object' == typeof arguments[0])
        settings = arguments[0];

    if (!settings.async && 'function' == typeof settings.beforeSend) {
        var args = arguments;

        settings.beforeSend();
        var dfd = $.Deferred();
        setTimeout(function() {
            $.orig_ajax.apply($, args).then(dfd.resolve)
                                      .fail(dfd.reject);
        } ,100);
        return dfd.promise();
    } else
        return $.orig_ajax.apply($, arguments);
};

не идеально (из-за другого отложенного объекта), но может быть полезно.

person salexch    schedule 21.04.2015