Comment faire pour que jQuery attende la fin d'un appel Ajax avant son retour?

244

J'ai une fonction côté serveur qui nécessite une connexion. Si l'utilisateur est connecté, la fonction renverra 1 en cas de succès. Sinon, la fonction renverra la page de connexion.

Je veux appeler la fonction en utilisant Ajax et jQuery. Ce que je fais, c'est soumettre la demande avec un lien ordinaire, avec une fonction de clic appliquée dessus. Si l'utilisateur n'est pas connecté ou que la fonction échoue, je veux que l'appel Ajax renvoie true, afin que le href se déclenche.

Cependant, lorsque j'utilise le code suivant, la fonction se ferme avant que l'appel Ajax ne soit effectué.

Comment puis-je rediriger gracieusement l'utilisateur vers la page de connexion?

$(".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
la source
2
Peut-être un vieux fil, mais comme le souligne @kofifus, définir async sur false est une mauvaise conception et "aucun délai d'expiration, etc. ne sera traité". Pourrait être essayer cette solution simplifiée - stackoverflow.com/a/11576418/6937841
Shan

Réponses:

357

Si vous ne souhaitez pas que la $.ajax()fonction revienne immédiatement, définissez l' asyncoption sur 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;
            }
        }
    });
});

Mais, je noterais que ce serait contraire au point d'AJAX. Vous devez également gérer la réponse dans les fonctions erroret success. Ces fonctions ne seront appelées que lorsque la réponse sera reçue du serveur.

cgp
la source
10
Transmettre les bonnes pratiques, à mon avis, ne juge pas et est la marque de certaines des meilleures réponses ici sur StackOverflow.
semperos
68
Ne jamais utiliser async: false. La boucle d'événements du navigateur se bloque pendant l'attente d'E / S réseau non fiables. Il y a toujours une meilleure façon. Dans ce cas, la cible du lien peut vérifier la session utilisateur et 302 sur la page de connexion.
Matthew
3
Pour les tests, async: falsepeut être très utile.
Jarrett
4
@Matthew Vraiment? Quelle est l'alternative à l'envoi d'un ajax avec async avant d'envoyer l'utilisateur vers une autre page ou de le rafraîchir?
NoBugs
2
asyncavec valeur falseest devenu obsolète dans la plupart des cas d'utilisation du navigateur. Il peut lever des exceptions dans une version plus récente des navigateurs. Voir xhr.spec.whatwg.org/#sync-warning (s'applique au asyncparamètre de la openméthode xhr , qui utilise jQuery).
Frédéric
42

Je n'utilise pas $.ajaxles fonctions $.postet $.get, donc si j'ai besoin d'attendre la réponse, j'utilise ceci:

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

L'objet XMLHttpRequest sous-jacent (utilisé par jQuery pour effectuer la demande) prend en charge la propriété asynchrone. Réglez-le sur faux . Comme

async: false
idroside
la source
24

Au lieu de définir async sur false, ce qui est généralement une mauvaise conception, vous pouvez envisager de bloquer l'interface utilisateur pendant que l'opération est en attente.

Cela peut être bien réalisé avec les promesses jQuery comme suit:

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

avec cela, vous pouvez maintenant faire:

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

Et l'interface utilisateur se bloquera jusqu'à ce que la commande ajax retourne

voir jsfiddle

kofifus
la source
Le fait de définir async sur false ne fait-il pas exactement la même chose, sauf dans une seule ligne de code?
Vincent
4
Pas du tout. Si vous définissez async sur false, la demande est asynchrone - c'est-à-dire qu'elle arrête le traitement jusqu'à ce qu'elle revienne, ce qui est généralement une mauvaise pratique, par exemple aucun événement, aucune autre demande ajax, délai d'expiration, etc. ne sera traité. Vous pouvez également modifier le code ci-dessus pour bloquer uniquement une partie de l'interface utilisateur pendant le traitement de votre ajax (c'est-à-dire la partie qu'il affectera)
kofifus
11

Je pense que les choses seraient plus faciles si vous codiez votre successfonction pour charger la page appropriée au lieu de retourner trueou false.

Par exemple, au lieu de revenir, truevous pouvez faire:

window.location="appropriate page";

De cette façon, lorsque la fonction de succès est appelée, la page est redirigée.

samuelagm
la source
5

Dans JS moderne, vous pouvez simplement utiliser async/ await, comme:

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

Appelez-le ensuite dans une asyncfonction comme:

let response = await upload();
Taohidul Islam
la source
1
Votre code ne fonctionnera pas ... vous êtes sur la bonne voie mais en l'état, il ne fonctionnera pas.
ppetree
Pouvez-vous développer davantage? Après avoir testé le code, j'ai répondu à cette question, cela devrait fonctionner.
Taohidul Islam
wait ne peut pas être utilisé en dehors de la fonction asynchrone, il génère une erreur.
ppetree
Oui, je l'ai mentionné dans l'avant-dernière ligne de ma réponse, "Alors appelez-le dans une asyncfonction".
Taohidul Islam
C'est une excellente solution, mais pour aider les autres à la recherche d'une excellente solution (comme je l'étais), vous devez modifier votre code et l'afficher de manière plus complète. Peut-être même inclure un lien vers un violon. Ce serait génial!
ppetree
0

Comme je ne le vois pas mentionné ici, j'ai pensé que je soulignerais également que l' instruction jQuery when peut être très utile à cet effet.

Leur exemple ressemble à ceci:

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

La partie "alors" ne s'exécutera pas avant la fin de la partie "quand".

Frank Hoffman
la source
0

Il devrait attendre la fin de la demande. Après cela, je reviendrai récupérer le corps de la requête d'où la fonction est appelée.

function foo() {
    var jqXHR = $.ajax({
        url: url,
        type: 'GET',
        async: false,
    });
    return JSON.parse(jqXHR.responseText);  
}
Hassan Ejaz
la source
Veuillez expliquer votre solution.
Abandon le
1
Ajout de @Dropout.
Hassan Ejaz
Merci @Hassan Ejaz! Les réponses qui n'ont pas d'explication et qui ne sont que du code sont signalées comme un faible effort.
Abandon le