Как заставить jQuery ждать завершения вызова Ajax до его возврата?

244

У меня есть функция на стороне сервера, которая требует входа в систему. Если пользователь вошел в систему, функция вернет 1 в случае успеха. Если нет, функция вернет страницу входа.

Я хочу вызвать функцию, используя Ajax и jQuery. Что я делаю, так это отправляю запрос с помощью обычной ссылки, к которой применена функция щелчка. Если пользователь не вошел в систему или функция не работает, я хочу, чтобы Ajax-вызов вернул true, чтобы сработал href.

Однако когда я использую следующий код, функция завершается до того, как будет выполнен вызов Ajax.

Как я могу изящно перенаправить пользователя на страницу входа?

$(".my_link").click(
    function(){
    $.ajax({
        url: $(this).attr('href'),
        type: 'GET',
        cache: false,
        timeout: 30000,
        error: function(){
            return true;
        },
        success: function(msg){ 
            if (parseFloat(msg)){
                return false;
            } else {
                return true;
            }
        }
    });
});
Хобхаус
источник
2
Возможно, это старая ветка, но, как указывает @kofifus, установка асинхронного значения на ложный - плохой дизайн, и «никакие тайм-ауты и т.д. не будут обрабатываться». Возможно, стоит попробовать это упрощенное решение - stackoverflow.com/a/11576418/6937841
Шань

Ответы:

357

Если вы не хотите, чтобы $.ajax()функция возвращалась немедленно, установите asyncпараметр false:

$(".my_link").click(
    function(){
    $.ajax({
        url: $(this).attr('href'),
        type: 'GET',
        async: false,
        cache: false,
        timeout: 30000,
        error: function(){
            return true;
        },
        success: function(msg){ 
            if (parseFloat(msg)){
                return false;
            } else {
                return true;
            }
        }
    });
});

Но я хотел бы отметить, что это противоречило бы AJAX. Кроме того , вы должны обработки ответа в errorи successфункциях. Эти функции будут вызываться только при получении ответа от сервера.

CGP
источник
10
Передача хороших практик, на мой взгляд, не является оценкой, и является признаком некоторых из лучших ответов здесь, на StackOverflow.
Semperos
68
Никогда не используйте async: false. Цикл событий браузера будет зависать при ожидании ненадежного сетевого ввода-вывода. Всегда есть лучший способ. В этом случае цель ссылки может подтвердить сеанс пользователя и 302 на страницу входа.
Матфея
3
Для тестирования async: falseможет быть очень полезно.
Джаррет
4
@ Мэтью Правда? Какая альтернатива отправке ajax с async перед отправкой пользователя на другую страницу или обновлением?
NoBugs
2
asyncсо значением falseбольше не рекомендуется в большинстве случаев использования браузера. Это может вызвать исключения в более новой версии браузеров. См. Xhr.spec.whatwg.org/#sync-warning (относится к asyncпараметру openметода xhr , который использует jQuery).
Фредерик
42

Я не использую , $.ajaxно в $.postи $.getфункции, поэтому если мне нужно ждать ответа, я использую это:

$.ajaxSetup({async: false});
$.get("...");
MilMike
источник
34

Базовый объект XMLHttpRequest (используемый jQuery для выполнения запроса) поддерживает асинхронное свойство. Установите это в ложь . подобно

async: false
idrosid
источник
24

Вместо установки async в false, что обычно является плохим дизайном, вы можете рассмотреть возможность блокировки пользовательского интерфейса во время ожидания операции.

Этого можно добиться с помощью обещаний jQuery следующим образом:

// same as $.ajax but settings can have a maskUI property
// if settings.maskUI==true, the UI will be blocked while ajax in progress
// if settings.maskUI is other than true, it's value will be used as the color value while bloking (i.e settings.maskUI='rgba(176,176,176,0.7)'
// in addition an hourglass is displayed while ajax in progress
function ajaxMaskUI(settings) {
    function maskPageOn(color) { // color can be ie. 'rgba(176,176,176,0.7)' or 'transparent'
        var div = $('#maskPageDiv');
        if (div.length === 0) {
            $(document.body).append('<div id="maskPageDiv" style="position:fixed;width:100%;height:100%;left:0;top:0;display:none"></div>'); // create it
            div = $('#maskPageDiv');
        }
        if (div.length !== 0) {
            div[0].style.zIndex = 2147483647;
            div[0].style.backgroundColor=color;
            div[0].style.display = 'inline';
        }
    }
    function maskPageOff() {
        var div = $('#maskPageDiv');
        if (div.length !== 0) {
            div[0].style.display = 'none';
            div[0].style.zIndex = 'auto';
        }
    }
    function hourglassOn() {
        if ($('style:contains("html.hourGlass")').length < 1) $('<style>').text('html.hourGlass, html.hourGlass * { cursor: wait !important; }').appendTo('head');
        $('html').addClass('hourGlass');
    }
    function hourglassOff() {
        $('html').removeClass('hourGlass');
    }

    if (settings.maskUI===true) settings.maskUI='transparent';

    if (!!settings.maskUI) {
        maskPageOn(settings.maskUI);
        hourglassOn();
    }

    var dfd = new $.Deferred();
    $.ajax(settings)
        .fail(function(jqXHR, textStatus, errorThrown) {
            if (!!settings.maskUI) {
                maskPageOff();
                hourglassOff();
            }
            dfd.reject(jqXHR, textStatus, errorThrown);
        }).done(function(data, textStatus, jqXHR) {
            if (!!settings.maskUI) {
                maskPageOff();
                hourglassOff();
            }
            dfd.resolve(data, textStatus, jqXHR);
        });

    return dfd.promise();
}

с этим теперь вы можете сделать:

ajaxMaskUI({
    url: url,
    maskUI: true // or try for example 'rgba(176,176,176,0.7)'
}).fail(function (jqXHR, textStatus, errorThrown) {
    console.log('error ' + textStatus);
}).done(function (data, textStatus, jqXHR) {
    console.log('success ' + JSON.stringify(data));
});

И пользовательский интерфейс будет блокироваться, пока не вернется команда ajax

смотри jsfiddle

kofifus
источник
Разве установка async в false не делает то же самое, кроме одной строки кода?
Винсент
4
Не за что. Если для параметра async установлено значение false, запрос будет асинхронным - то есть остановка обработки до тех пор, пока он не вернется, что обычно является плохой практикой, например, никакие события, другие запросы ajax, тайм-ауты и т. Д. Не будут обрабатываться. Вы также можете изменить приведенный выше код, чтобы блокировать только часть пользовательского интерфейса во время обработки вашего ajax (то есть части, на которую он повлияет)
kofifus,
11

Я думаю, что было бы проще, если бы вы кодировали свою successфункцию для загрузки соответствующей страницы вместо возврата trueили false.

Например, вместо возврата trueвы можете сделать:

window.location="appropriate page";

Таким образом, когда вызывается функция успеха, страница перенаправляется.

samuelagm
источник
5

В современном JS вы можете просто использовать async/ await, например:

  async function upload() {
    return new Promise((resolve, reject) => {
        $.ajax({
            url: $(this).attr('href'),
            type: 'GET',
            timeout: 30000,
            success: (response) => {
                resolve(response);
            },
            error: (response) => {
                reject(response);
            }
        })
    })
}

Тогда вызовите это в asyncфункции как:

let response = await upload();
Таохидул Ислам
источник
1
Ваш код не сработает ... вы на правильном пути, но на самом деле он не сработает.
ppetree
Можете ли вы рассказать подробнее? После тестирования кода я ответил на этот вопрос, он должен работать.
Таохидул Ислам
await не может быть использован вне асинхронной функции, он выдает ошибку.
ppetree
Да, я упоминал об этом во второй последней строке моего ответа: «Затем вызовите это в asyncфункции».
Таохидул Ислам
Это отличное решение, но чтобы помочь другим, ищущим отличное решение (как я), вы должны отредактировать свой код и показать его более полно. Возможно, даже включить ссылку на скрипку. Это было бы круто!
ppetree
0

Поскольку я не вижу здесь упоминания, я подумал также, что оператор jQuery when может быть очень полезен для этой цели.

Их пример выглядит так:

$.when( $.ajax( "test.aspx" ) ).then(function( data, textStatus, jqXHR ) {
  alert( jqXHR.status ); // Alerts 200
});

Часть «then» не будет выполнена, пока не закончится часть «when».

Фрэнк Хоффман
источник
0

Следует подождать, пока запрос не будет завершен. После этого я верну тело запроса get, откуда вызывается функция.

function foo() {
    var jqXHR = $.ajax({
        url: url,
        type: 'GET',
        async: false,
    });
    return JSON.parse(jqXHR.responseText);  
}
Хасан Эджаз
источник
Пожалуйста, объясните ваше решение.
Выпуск
1
Добавлен @Dropout.
Хасан Эджаз
Спасибо @Hassan Ejaz! Ответы, которые не имеют объяснения и являются только кодом, помечаются как легкие.
Выпуск