Come posso fare in modo che jQuery attenda il termine di una chiamata Ajax prima che ritorni?


244

Ho una funzione lato server che richiede il login. Se l'utente ha effettuato l'accesso, la funzione restituirà 1 in caso di successo. In caso contrario, la funzione restituirà la pagina di accesso.

Voglio chiamare la funzione usando Ajax e jQuery. Quello che faccio è inviare la richiesta con un link normale, con una funzione click applicata su di essa. Se l'utente non ha effettuato l'accesso o la funzione ha esito negativo, desidero che la chiamata Ajax ritorni vera, in modo che l'href si attivi.

Tuttavia, quando utilizzo il seguente codice, la funzione termina prima che venga eseguita la chiamata Ajax.

Come posso reindirizzare l'utente con garbo alla pagina di accesso?

$(".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
Potrebbe essere un vecchio thread, ma come indica @kofifus l'impostazione di asincronizzazione su false è un progetto errato e "non verranno elaborati timeout ecc.". Potrebbe essere provare questa soluzione semplificata - stackoverflow.com/a/11576418/6937841
Shan

Risposte:


357

Se non si desidera che la $.ajax()funzione ritorni immediatamente, impostare l' asyncopzione su 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;
            }
        }
    });
});

Tuttavia, noterei che questo sarebbe contrario al punto di AJAX. Inoltre, dovresti gestire la risposta nelle funzioni errore success. Tali funzioni verranno chiamate solo quando la risposta viene ricevuta dal server.


10
Trasmettere buone pratiche, secondo me, non è giudicare, ed è il segno di alcune delle migliori risposte qui su StackOverflow.
sempre

68
Non utilizzare mai async: false. Il loop degli eventi del browser si bloccherà durante l'attesa di I / O di rete inaffidabili. C'è sempre un modo migliore. In questo caso, la destinazione del collegamento può verificare la sessione utente e 302 alla pagina di accesso.
Matteo,

3
Per i test, async: falsepuò essere molto utile.
Jarrett,

4
@Matthew Davvero? Qual è l'alternativa all'invio di un Ajax con Async prima di inviare l'utente ad un'altra pagina o aggiornare?
NoBugs,

2
asynccon valore falseè diventato obsoleto nella maggior parte dei casi d'uso del browser. Potrebbe generare eccezioni nella versione più recente dei browser. Vedi xhr.spec.whatwg.org/#sync-warning (si applica al asyncparametro del openmetodo xhr , che è ciò che utilizza jQuery).
Frédéric,

42

Non sto usando $.ajaxma le funzioni $.poste $.get, quindi se devo aspettare la risposta, uso questo:

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

34

L'oggetto XMLHttpRequest sottostante (utilizzato da jQuery per effettuare la richiesta) supporta la proprietà asincrona. Impostalo su false . Piace

async: false

24

Invece di impostare asincrono su falso, che di solito è una cattiva progettazione, potresti voler considerare di bloccare l'interfaccia utente mentre l'operazione è in sospeso.

Questo può essere ottenuto con le promesse di jQuery come segue:

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

con questo ora puoi fare:

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 l'interfaccia utente verrà bloccata fino a quando non verrà restituito il comando Ajax

vedi jsfiddle


L'impostazione di async su false non fa esattamente la stessa cosa se non in una singola riga di codice?
Vincent,

4
Affatto. L'impostazione di async su false rende asincrona la richiesta, ovvero interrompe l'elaborazione fino a quando non ritorna, che di solito è una cattiva pratica, ad esempio nessun evento, altre richieste ajax, timeout ecc. Verranno elaborati. Puoi anche modificare il codice sopra per bloccare solo una parte dell'interfaccia utente mentre il tuo ajax è in elaborazione (ovvero la parte che influenzerà)
kofifus,

11

Penso che le cose sarebbero più facili se codifichi la tua successfunzione per caricare la pagina appropriata invece di tornare trueo false.

Ad esempio invece di tornare truepuoi fare:

window.location="appropriate page";

In questo modo, quando viene chiamata la funzione di successo, la pagina viene reindirizzata.


5

Nel moderno JS puoi semplicemente usare async/ await, come:

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

Quindi chiamalo in una asyncfunzione come:

let response = await upload();

1
Il tuo codice non funzionerà ... sei sulla buona strada ma così com'è, non funzionerà.
ppetree,

Potete per favore elaborare di più? Dopo aver testato il codice ho risposto a questa domanda, dovrebbe funzionare.
Taohidul Islam

waitit non può essere utilizzato al di fuori della funzione asincrona, genera un errore.
ppetree

Sì, l'ho menzionato nell'ultima riga della mia risposta, "Quindi chiamalo in una asyncfunzione".
Taohidul Islam

È un'ottima soluzione, ma per aiutare gli altri a cercare un'ottima soluzione (come lo ero io), dovresti modificare il tuo codice e mostrarlo in un modo più completo. Forse anche includere un collegamento a un violino. Sarebbe fantastico!
ppetree

0

Dal momento che non lo vedo menzionato qui, ho pensato di sottolineare anche che l' istruzione jQuery when può essere molto utile per questo scopo.

Il loro esempio è simile al seguente:

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

La parte "then" non verrà eseguita fino alla fine della parte "when".


0

Dovrebbe attendere fino al completamento della richiesta. Dopodiché tornerò ottenere il corpo della richiesta da dove viene chiamata la funzione.

function foo() {
    var jqXHR = $.ajax({
        url: url,
        type: 'GET',
        async: false,
    });
    return JSON.parse(jqXHR.responseText);  
}

Per favore, spiega la tua soluzione.
Abbandono il

1
Aggiunto @Dropout.
Hassan Ejaz,

Grazie @Hassan Ejaz! Le risposte che non hanno una spiegazione e sono solo codice vengono contrassegnate come a basso sforzo.
Abbandono il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.