Jak sprawić, aby jQuery czekał na zakończenie połączenia Ajax, zanim ono zwróci?

244

Mam funkcję po stronie serwera, która wymaga logowania. Jeśli użytkownik jest zalogowany, funkcja zwróci 1 po pomyślnym zakończeniu. Jeśli nie, funkcja zwróci stronę logowania.

Chcę wywołać funkcję za pomocą Ajax i jQuery. To, co robię, to przesłanie żądania zwykłym linkiem z zastosowaną funkcją kliknięcia. Jeśli użytkownik nie jest zalogowany lub funkcja nie powiedzie się, chcę, aby wywołanie Ajax zwróciło wartość true, aby uruchomił się href.

Jednak gdy użyję następującego kodu, funkcja kończy działanie przed wykonaniem wywołania Ajax.

Jak mogę z wdziękiem przekierować użytkownika na stronę logowania?

$(".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
źródło
2
Może to być stary wątek, ale jak wskazuje @ kofifus, ustawienie asynchronizacji na false jest złym projektem i „nie zostaną przetworzone żadne limity czasu itp.”. Może warto wypróbować to uproszczone rozwiązanie - stackoverflow.com/a/11576418/6937841
Shan

Odpowiedzi:

357

Jeśli nie chcesz, aby $.ajax()funkcja natychmiast wracała, ustaw asyncopcję na 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;
            }
        }
    });
});

Chciałbym jednak zauważyć, że byłoby to sprzeczne z punktem AJAX. Powinieneś także obsługiwać odpowiedzi w funkcjach errori success. Funkcje te będą wywoływane dopiero po otrzymaniu odpowiedzi z serwera.

cgp
źródło
10
Moim zdaniem przekazywanie dobrych praktyk nie jest osądzaniem i jest znakiem niektórych z najlepszych odpowiedzi tutaj na StackOverflow.
semperos
68
Nigdy nie należy używać async: false. Pętla zdarzeń przeglądarki zawiesi się podczas oczekiwania na niewiarygodne sieciowe operacje we / wy. Zawsze jest lepszy sposób. W takim przypadku cel łącza może zweryfikować sesję użytkownika i 302 do strony logowania.
Mateusz
3
Do testowania async: falsemoże być bardzo przydatny.
Jarrett
4
@ Matthew Naprawdę? Jaka jest alternatywa dla wysyłania ajax z asynchronią przed wysłaniem użytkownika na inną stronę lub odświeżeniem?
NoBugs
2
asyncwartość falsejest przestarzała w większości przypadków użycia przeglądarki. Może powodować wyjątki w nowszych wersjach przeglądarek. Zobacz xhr.spec.whatwg.org/#sync-warning (dotyczy asyncparametru openmetody xhr , która używa jQuery).
Frédéric
42

Nie korzystam $.ajaxz funkcji $.posti $.get, więc jeśli muszę czekać na odpowiedź, używam tego:

$.ajaxSetup({async: false});
$.get("...");
MilMike
źródło
34

Podstawowy obiekt XMLHttpRequest (używany przez jQuery do wykonania żądania) obsługuje właściwość asynchroniczną. Ustaw na false . Lubić

async: false
idrosid
źródło
24

Zamiast ustawiać async na false, co jest zwykle złym projektem, możesz rozważyć zablokowanie interfejsu użytkownika podczas operacji.

Można to łatwo osiągnąć dzięki obietnicom jQuery w następujący sposób:

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

dzięki temu możesz teraz:

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

Interfejs użytkownika będzie blokował się do momentu powrotu polecenia ajax

patrz jsfiddle

Kofifus
źródło
Czy ustawienie asynchronicznej wartości false nie robi dokładnie tego samego, z wyjątkiem jednego wiersza kodu?
Vincent,
4
Ani trochę. Ustawienie async na false powoduje, że żądanie staje się asynchroniczne - oznacza to, że przetwarzanie jest zatrzymywane, dopóki nie zostanie zwrócone, co zwykle jest złą praktyką, na przykład nie będą przetwarzane żadne zdarzenia, inne żądania ajax, limity czasu itp. Możesz również zmodyfikować powyższy kod, aby blokował tylko część interfejsu użytkownika podczas przetwarzania Twojego
aukcji
11

Myślę, że byłoby łatwiej, jeśli kodujesz swoją successfunkcję, aby załadować odpowiednią stronę zamiast zwracać truelub false.

Na przykład zamiast zwrotu truemożesz:

window.location="appropriate page";

W ten sposób, gdy wywoływana jest funkcja sukcesu, strona zostaje przekierowana.

samuelagm
źródło
5

W nowoczesnym JS możesz po prostu użyć async/ await, np .:

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

Następnie wywołaj go w asyncfunkcji takiej jak:

let response = await upload();
Taohidul Islam
źródło
1
Twój kod nie będzie działał ... jesteś na dobrej drodze, ale w tej chwili nie będzie działać.
ppetree
Czy możesz proszę opracować więcej? Po przetestowaniu kodu odpowiedziałem na to pytanie, powinno działać.
Taohidul Islam
Oczekiwanie nie może być używane poza funkcją asynchroniczną, generuje błąd.
ppetree
Tak, wspomniałem o tym w drugim ostatnim wierszu mojej odpowiedzi: „Wywołaj to w asyncfunkcji”.
Taohidul Islam
To świetne rozwiązanie, ale aby pomóc innym, którzy szukają świetnego rozwiązania (tak jak ja), powinieneś edytować swój kod i pokazać go w bardziej kompletny sposób. Może nawet zawierać link do skrzypiec. To byłoby niesamowite!
ppetree
0

Ponieważ nie widzę tu wspomnianego, pomyślałem, że zwrócę również uwagę na to, że instrukcja jQuery when może być bardzo przydatna w tym celu.

Ich przykład wygląda następująco:

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

Część „wtedy” nie zostanie wykonana, dopóki część „kiedy” się nie skończy.

Frank Hoffman
źródło
0

Powinien poczekać, aż żądanie zostanie zakończone. Następnie zwrócę get body żądania z miejsca, w którym wywoływana jest funkcja.

function foo() {
    var jqXHR = $.ajax({
        url: url,
        type: 'GET',
        async: false,
    });
    return JSON.parse(jqXHR.responseText);  
}
Hassan Ejaz
źródło
Proszę wyjaśnić swoje rozwiązanie.
Rezygnacja
1
Dodano @Dropout.
Hassan Ejaz
Dzięki @Hassan Ejaz! Odpowiedzi, które nie zawierają wyjaśnień i są tylko kodem, są oznaczane jako mało wymagające.
Rezygnacja