Como faço para o jQuery aguardar a conclusão de uma chamada do Ajax antes de retornar?

243

Eu tenho uma função do lado do servidor que requer login. Se o usuário estiver logado, a função retornará 1 em caso de sucesso. Caso contrário, a função retornará a página de login.

Eu quero chamar a função usando Ajax e jQuery. O que faço é enviar a solicitação com um link comum, com uma função de clique aplicada. Se o usuário não estiver conectado ou a função falhar, desejo que a chamada Ajax retorne true, para que o href seja acionado.

No entanto, quando eu uso o código a seguir, a função sai antes que a chamada do Ajax seja concluída.

Como redirecionar o usuário normalmente para a página de login?

$(".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;
            }
        }
    });
});
Hobhouse
fonte
2
Pode ser um encadeamento antigo, mas como o @kofifus aponta que definir assíncrono como falso é um design ruim e "nenhum tempo limite etc será processado". Poderia ser tentar esta solução simplificada - stackoverflow.com/a/11576418/6937841
Shan

Respostas:

355

Se você não deseja que a $.ajax()função retorne imediatamente, defina a asyncopção para 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;
            }
        }
    });
});

Mas, eu observaria que isso seria contrário ao ponto do AJAX. Além disso, você deve lidar com a resposta nas funções errore success. Essas funções serão chamadas somente quando a resposta for recebida do servidor.

cgp
fonte
10
A transmissão de boas práticas, na minha opinião, não está julgando e é a marca de algumas das melhores respostas aqui no StackOverflow.
Semperos
68
Nunca use async: false. O loop de eventos do navegador será interrompido enquanto aguarda E / S de rede não confiável. Sempre há uma maneira melhor. Nesse caso, o destino do link pode verificar a sessão do usuário e 302 na página de login.
Matthew
3
Para teste, async: falsepode ser muito útil.
Jarrett
4
@Matthew Realmente? Qual é a alternativa para enviar um ajax com async antes de enviar o usuário para outra página ou atualizar?
NoBugs
2
asynccom valor falsefoi descontinuado na maioria dos casos de uso do navegador. Pode lançar exceções na versão mais recente dos navegadores. Consulte xhr.spec.whatwg.org/#sync-warning (aplica-se ao asyncparâmetro do openmétodo xhr , que é o que usa jQuery).
Frédéric
42

Não estou usando, $.ajaxmas as funções $.poste $.get, portanto, se precisar aguardar a resposta, utilizo o seguinte:

$.ajaxSetup({async: false});
$.get("...");
MilMike
fonte
34

O objeto XMLHttpRequest subjacente (usado pelo jQuery para fazer a solicitação) suporta a propriedade assíncrona. Defina como falso . Gostar

async: false
idrosid
fonte
24

Em vez de definir async como false, que geralmente é um design ruim, considere bloquear a interface do usuário enquanto a operação está pendente.

Isso pode ser bem conseguido com as promessas do jQuery da seguinte maneira:

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

Com isso, agora você pode fazer:

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

E a interface do usuário bloqueará até que o comando ajax retorne

veja jsfiddle

kofifus
fonte
A configuração de async como false não faz exatamente a mesma coisa, exceto em uma única linha de código?
Vincent
4
De modo nenhum. Definir async como false torna a solicitação assíncrona - isso interrompe o processamento até que retorne, o que geralmente é uma má prática, por exemplo, nenhum evento, outras solicitações de ajax, tempos limite etc. serão processados. Você também pode modificar o código acima para bloquear apenas uma parte da interface do usuário enquanto o Ajax está processando (ou seja, a parte que irá afectar)
kofifus
11

Eu acho que as coisas seriam mais fáceis se você codificar sua successfunção para carregar a página apropriada em vez de retornar trueou false.

Por exemplo, em vez de retornar, truevocê poderia fazer:

window.location="appropriate page";

Dessa forma, quando a função de sucesso é chamada, a página é redirecionada.

samuelagm
fonte
5

No JS moderno, você pode simplesmente usar async/ await, como:

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

Em seguida, chame-o em uma asyncfunção como:

let response = await upload();
Taohidul Islam
fonte
1
Seu código não funcionará ... você está no caminho certo, mas, como está, não funcionará.
ppetree 27/03
Você pode elaborar mais? Depois de testar o código, eu respondi a essa pergunta, ele deve funcionar.
Taohidul Islam 27/03
waitit não pode ser usado fora da função assíncrona, gera um erro.
ppetree 29/03
Sim, eu mencionei isso na segunda última linha da minha resposta, "Então chame isso em uma asyncfunção".
Taohidul Islam 29/03
É uma ótima solução, mas para ajudar outras pessoas que procuram uma ótima solução (como eu era), edite seu código e mostre-o de uma maneira mais completa. Talvez até inclua um link para um violino. Isso seria demais!
ppetree em 30/03
0

Como não o vejo mencionado aqui, pensei em apontar que a instrução jQuery when pode ser muito útil para esse propósito.

O exemplo deles é assim:

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

A parte "then" não será executada até que a parte "when" termine.

Frank Hoffman
fonte
0

Deve esperar até que a solicitação seja concluída. Depois disso, retornarei o corpo da solicitação get de onde a função é chamada.

function foo() {
    var jqXHR = $.ajax({
        url: url,
        type: 'GET',
        async: false,
    });
    return JSON.parse(jqXHR.responseText);  
}
Hassan Ejaz
fonte
Por favor, explique sua solução.
Dropout
1
Adicionado @Dropout.
Hassan Ejaz 16/06
Obrigado @Hassan Ejaz! Respostas que não têm explicação e são apenas códigos são sinalizadas como pouco esforço.
Dropout