Comment puis-je demander à jQuery d'effectuer une requête Ajax synchrone plutôt qu'asynchrone?

1208

J'ai un widget JavaScript qui fournit des points d'extension standard. L'un d'eux est la beforecreatefonction. Il doit revenir falsepour empêcher la création d'un élément.

J'ai ajouté un appel Ajax dans cette fonction en utilisant jQuery:

beforecreate: function (node, targetNode, type, to) {
  jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),

  function (result) {
    if (result.isOk == false) 
        alert(result.message);
  });
}

Mais je veux empêcher mon widget de créer l'élément, donc je devrais revenir falsedans la fonction mère, pas dans le rappel. Existe-t-il un moyen d'effectuer une demande AJAX synchrone à l'aide de jQuery ou de toute autre API dans le navigateur?

Artem Tikhomirov
la source
30
La bonne façon de résoudre ce problème serait de réécrire les promesses d'utilisation du point d'extension de votre widget. De cette façon, vous pourriez facilement configurer une action asynchrone (comme une demande ajax) en tant que beforecreate.
Kos
3
Je propose la fonction Sajak. Avons-nous une lettre T? Oui vanna, donnez-moi 3 T.
Cody O'Dell
2
@Kos est parfait. La synchronisation XHR n'est pas requise ici
Michael Westcott
1
Si les gens me demandent 1 conseil au démarrage de javascript, je dis: adoptez le caractère asynchrone de javascript, n'essayez pas de le combattre.
Emmanuel Delay
3
Cette question revient à demander "comment stocker mes mots de passe utilisateur en texte brut?" Bien sûr, il y a une réponse, mais ne le faites pas.
Vectorjohn

Réponses:

1165

Dans la documentation jQuery : vous spécifiez l' option asynchrone sur false pour obtenir une requête Ajax synchrone. Ensuite, votre rappel peut définir certaines données avant que votre fonction mère continue.

Voici à quoi ressemblerait votre code s'il était modifié comme suggéré:

beforecreate: function (node, targetNode, type, to) {
    jQuery.ajax({
        url: 'http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),
        success: function (result) {
            if (result.isOk == false) alert(result.message);
        },
        async: false
    });
}
Adam Bellaire
la source
97
Exactement, il est impossible d'utiliser get (), post (), load () pour les appels synchrones. Seul ajax () a le paramètre "async", qui peut être défini sur "false".
SLA80
39
@ SLA80 Non. Depuis jQuery 1.1: stackoverflow.com/questions/6849686/…
StuperUser
16
@qualidafial mon commentaire vise le commentaire incorrect de SLA80; il est possible d'utiliser get (), post (), load () pour les appels synchrones.
StuperUser
9
async false n'est plus pris en charge pour jQuery> = 1.8. Reportez-vous à api.jquery.com/jQuery.ajax
ken
22
@ken async: falseest toujours pris en charge dans jQuery> = 1.8. La capacité à utiliser async: falseavec jqXHR ( $.Deferred) est ce qui était déconseillé. En d'autres termes, il faut utiliser les nouvelles options de rappel succès / erreur / complet, au lieu des méthodes héritées, par exemple jqXHR.done().
Ben Johnson
255

Vous pouvez mettre la configuration Ajax de jQuery en mode synchrone en appelant

jQuery.ajaxSetup({async:false});

Et puis effectuez vos appels Ajax en utilisant jQuery.get( ... );

Puis je le rallume une fois

jQuery.ajaxSetup({async:true});

Je suppose que cela fonctionne de la même manière que suggéré par @Adam, mais cela pourrait être utile à quelqu'un qui veut reconfigurer sa syntaxe jQuery.get()ou jQuery.post()la jQuery.ajax()syntaxe plus élaborée .

Sydwell
la source
56
C'est une très mauvaise idée, pourquoi la définiriez-vous au niveau mondial? Et puis être obligé de le désarmer après?
Juan Mendes
11
Du moins, c'est la meilleure mauvaise idée. au lieu de dire: "IL N'Y A AUCUNE MANIÈRE SAUF $.ajax()". ;)
Rzassar
136

Excellente solution! J'ai remarqué lorsque j'ai essayé de l'implémenter que si je retournais une valeur dans la clause de réussite, elle revenait comme non définie. J'ai dû le stocker dans une variable et retourner cette variable. Voici la méthode que j'ai trouvée:

function getWhatever() {
  // strUrl is whatever URL you need to call
  var strUrl = "", strReturn = "";

  jQuery.ajax({
    url: strUrl,
    success: function(html) {
      strReturn = html;
    },
    async:false
  });

  return strReturn;
}
James à Indy
la source
16
C'est parce que vous renvoyiez une valeur du rappel, pas du getWhatever. Ainsi vous n'avez rien retourné, c'est undefinedà dire de votre getWhatever.
Courses de légèreté en orbite le
6
c'est absolument faux. Vous ne pouvez pas être sûr qu'un appel asynchrone s'est terminé lors du retour de 'strReturn'.
Thomas Fritz
61
Il s'agit d'un appel synchrone (async: false).
James à Indy
85

Toutes ces réponses passent à côté du fait que faire un appel Ajax avec async: false provoquera le blocage du navigateur jusqu'à ce que la demande Ajax se termine. L'utilisation d'une bibliothèque de contrôle de flux résoudra ce problème sans raccrocher le navigateur. Voici un exemple avec Frame.js :

beforecreate: function(node,targetNode,type,to) {

    Frame(function(next)){

        jQuery.get('http://example.com/catalog/create/', next);
    });

    Frame(function(next, response)){

        alert(response);
        next();
    });

    Frame.init();
}
BishopZ
la source
4
Les appels synchrones sont pratiques si vous souhaitez mettre en place un faisceau de test rapide pour un back-end REST et préférez la simplicité à l'enfer de rappel.
Distortum
50
function getURL(url){
    return $.ajax({
        type: "GET",
        url: url,
        cache: false,
        async: false
    }).responseText;
}


//example use
var msg=getURL("message.php");
alert(msg);
Carcione
la source
10
Notez que cependant, vous pourriez obtenir ceci: 'Synchronous XMLHttpRequest sur le thread principal est obsolète en raison de ses effets néfastes sur l'expérience de l'utilisateur final.'
Hari
5
@Hari Quelle est la façon d'effectuer un appel ajax synchrone en utilisant jQuery sans utiliser le thread principal?
Jose Nobile
7
Je ne vois pas que cette réponse a quelque chose à offrir, c'est juste la réponse acceptée enveloppée dans une fonction et répondue quatre ans plus tard. C'est une mauvaise chose de conseiller aux gens de C + P, car la fonction n'indique pas ce qu'elle fait. "Alors getURLvs get? Pourquoi on bloque mon navigateur?" etc.
moopet
27

Remarque: vous ne devez pas utiliser async: falsece message d'avertissement:

Depuis Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), les requêtes synchrones sur le thread principal sont obsolètes en raison des effets négatifs sur l'expérience utilisateur.

Chrome avertit même à ce sujet dans la console:

Synchronous XMLHttpRequest sur le thread principal est obsolète en raison de ses effets néfastes sur l'expérience de l'utilisateur final. Pour plus d'aide, consultez https://xhr.spec.whatwg.org/ .

Cela pourrait casser votre page si vous faites quelque chose comme ça, car cela pourrait cesser de fonctionner n'importe quel jour.

Si vous voulez le faire d'une manière qui semble toujours synchrone mais qui ne bloque toujours pas, vous devez utiliser async / wait et probablement aussi un ajax basé sur des promesses comme la nouvelle API Fetch

async function foo() {
  var res = await fetch(url)
  console.log(res.ok)
  var json = await res.json()
  console.log(json)
}

Modifier Chrome travaille sur l' interdiction de la synchronisation XHR dans le rejet de page lorsque la page est en cours de navigation ou fermée par l'utilisateur. Cela implique avant le déchargement, le déchargement, le masquage de page et le changement de visibilité.

si tel est votre cas d'utilisation, vous voudrez peut-être jeter un œil à navigator.sendBeacon place

Il est également possible pour la page de désactiver la demande de synchronisation avec les en-têtes http ou l'attribut allow iframe

Interminable
la source
Notez que vous devez encapsuler votre await fetch(url)appel dans un try... catchbloc pour intercepter toutes les erreurs asynchrones.
inostia
4
la fonction foo a été convertie en promesse en utilisant simplement le mot asyncau début, vous pouvez donc simplement l' foo().catch(...)attraper également
Endless
Chrome a déjà interdit la page intérieure ajax asynchrone, beforeunloadmais a ensuite annulé la modification après des commentaires négatifs bugs.chromium.org/p/chromium/issues/detail?id=952452
Alex
26

Gardez à l'esprit que si vous effectuez un appel Ajax interdomaine (en utilisant JSONP ) - vous ne pouvez pas le faire de manière synchrone, l' asyncindicateur sera ignoré par jQuery.

$.ajax({
    url: "testserver.php",
    dataType: 'jsonp', // jsonp
    async: false //IGNORED!!
});

Pour les appels JSONP, vous pouvez utiliser:

  1. Ajax appelez vers votre propre domaine - et effectuez l'appel inter-domaine côté serveur
  2. Modifiez votre code pour qu'il fonctionne de manière asynchrone
  3. Utilisez une bibliothèque de "séquenceurs de fonctions" comme Frame.js (cette réponse )
  4. Bloquer l'interface utilisateur au lieu de bloquer l'exécution (cette réponse ) (ma façon préférée)
Serge Shultz
la source
12

Avec async: falsevous obtenez un navigateur bloqué. Pour une solution synchrone non bloquante, vous pouvez utiliser les éléments suivants:

ES6 / ECMAScript2015

Avec ES6, vous pouvez utiliser un générateur et la bibliothèque commune :

beforecreate: function (node, targetNode, type, to) {
    co(function*(){  
        let result = yield jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value));
        //Just use the result here
    });
}

ES7

Avec ES7, vous pouvez simplement utiliser asyc attendre:

beforecreate: function (node, targetNode, type, to) {
    (async function(){
        let result = await jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value));
        //Just use the result here
    })(); 
}
Spenhouet
la source
3
Remarquez que vous pouvez simplement passer async functionà beforecreate beforecreate: async function(....et supprimer la fonction asynchrone auto-invoquée
Daniel Krom
12

J'ai utilisé la réponse donnée par Carcione et l'ai modifiée pour utiliser JSON.

 function getUrlJsonSync(url){

    var jqxhr = $.ajax({
        type: "GET",
        url: url,
        dataType: 'json',
        cache: false,
        async: false
    });

    // 'async' has to be 'false' for this to work
    var response = {valid: jqxhr.statusText,  data: jqxhr.responseJSON};

    return response;
}    

function testGetUrlJsonSync()
{
    var reply = getUrlJsonSync("myurl");

    if (reply.valid == 'OK')
    {
        console.dir(reply.data);
    }
    else
    {
        alert('not valid');
    }    
}

J'ai ajouté le dataType de 'JSON' et changé le .responseText en responseJSON .

J'ai également récupéré le statut en utilisant la propriété statusText de l'objet retourné. Notez qu'il s'agit de l'état de la réponse Ajax, et non de la validité du JSON.

Le back-end doit retourner la réponse dans un JSON correct (bien formé), sinon l'objet retourné ne sera pas défini.

Il y a deux aspects à considérer lors de la réponse à la question d'origine. L'une dit à Ajax de fonctionner de manière synchrone (en définissant async: false ) et l'autre renvoie la réponse via l'instruction return de la fonction appelante, plutôt que dans une fonction de rappel.

Je l'ai également essayé avec POST et cela a fonctionné.

J'ai changé le GET en POST et ajouté des données: postdata

function postUrlJsonSync(url, postdata){

    var jqxhr = $.ajax({
        type: "POST",
        url: url,
        data: postdata,
        dataType: 'json',
        cache: false,
        async: false
    });

    // 'async' has to be 'false' for this to work
    var response = {valid: jqxhr.statusText,  data: jqxhr.responseJSON};

    return response;
}

Notez que le code ci-dessus ne fonctionne que dans le cas où async est faux . Si vous deviez définir async: true, l'objet renvoyé jqxhr ne serait pas valide au moment du retour de l'appel AJAX, uniquement plus tard lorsque l'appel asynchrone est terminé, mais il est beaucoup trop tard pour définir la variable de réponse .

paulo62
la source
7

Voici un exemple:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});
recherche9x
la source
1
Quel est l'intérêt d'utiliser async falseavec une solution basée sur des promesses. Cela ne fait que verrouiller le navigateur sans aucun avantage.
Gone Coding
2
@GoneCoding il y a des situations où vous voulez exécuter du code dans un ordre exact, dans cette situation, nous pouvons utiliser async false
Nikki
1
@Nikki: Ce n'est absolument pas la façon de les exécuter séquentiellement. Utilisez des promesses asynchrones (dont les ajaxappels reviennent déjà) et .then()ou done()(comme il le montre déjà). Le verrouillage du navigateur est une expérience utilisateur très médiocre. Comme je l'ai dit, avoir async: falsedans cet exemple est une perte de temps totale.
Fin du codage
4

Premièrement, nous devons comprendre quand nous utilisons $ .ajax et quand nous utilisons $ .get / $. Post

Lorsque nous avons besoin d'un contrôle de bas niveau sur la demande ajax, comme les paramètres d'en-tête de demande, les paramètres de mise en cache, les paramètres synchrones, etc., nous devrions opter pour $ .ajax.

$ .get / $. post: lorsque nous n'exigeons pas un contrôle de bas niveau sur la demande ajax.Seulement obtenir / publier les données sur le serveur. C'est un raccourci de

$.ajax({
  url: url,
  data: data,
  success: success,
  dataType: dataType
});

et donc nous ne pouvons pas utiliser d'autres fonctionnalités (synchronisation, cache, etc.) avec $ .get / $. post.

Par conséquent, pour un contrôle de bas niveau (synchronisation, cache, etc.) sur la demande ajax, nous devrions opter pour $ .ajax

 $.ajax({
     type: 'GET',
      url: url,
      data: data,
      success: success,
      dataType: dataType,
      async:false
    });
Sheo Dayal Singh
la source
2

c'est ma simple implémentation pour les requêtes ASYNC avec jQuery. J'espère que cela aidera n'importe qui.

var queueUrlsForRemove = [
    'http://dev-myurl.com/image/1', 
    'http://dev-myurl.com/image/2',
    'http://dev-myurl.com/image/3',
];

var queueImagesDelete = function(){

    deleteImage( queueUrlsForRemove.splice(0,1), function(){
        if (queueUrlsForRemove.length > 0) {
            queueImagesDelete();
        }
    });

}

var deleteImage = function(url, callback) {
    $.ajax({
        url: url,
        method: 'DELETE'
    }).done(function(response){
        typeof(callback) == 'function' ? callback(response) : null;
    });
}

queueImagesDelete();
Felipe Marques
la source
2

Parce que le XMLHttpReponsefonctionnement synchrone est obsolète, j'ai proposé la solution suivante qui se termine XMLHttpRequest. Cela permet des requêtes AJAX ordonnées tout en étant de nature asycrone, ce qui est très utile pour les jetons CSRF à usage unique.

Il est également transparent afin que les bibliothèques telles que jQuery fonctionnent de manière transparente.

/* wrap XMLHttpRequest for synchronous operation */
var XHRQueue = [];
var _XMLHttpRequest = XMLHttpRequest;
XMLHttpRequest = function()
{
  var xhr   = new _XMLHttpRequest();
  var _send = xhr.send;

  xhr.send = function()
  {
    /* queue the request, and if it's the first, process it */
    XHRQueue.push([this, arguments]);
    if (XHRQueue.length == 1)
      this.processQueue();
  };

  xhr.processQueue = function()
  {
    var call = XHRQueue[0];
    var xhr  = call[0];
    var args = call[1];

    /* you could also set a CSRF token header here */

    /* send the request */
    _send.apply(xhr, args);
  };

  xhr.addEventListener('load', function(e)
  {
    /* you could also retrieve a CSRF token header here */

    /* remove the completed request and if there is more, trigger the next */
    XHRQueue.shift();
    if (XHRQueue.length)
      this.processQueue();
  });

  return xhr;
};
Geoffrey
la source
-1

Étant donné que la question d'origine portait sur jQuery.get, il convient de mentionner ici que (comme mentionné ici ), on pourrait l' utiliser async: falsedans un $.get()mais idéalement l'éviter car asynchrone XMLHTTPRequestest déconseillé (et le navigateur peut donner un avertissement):

$.get({
  url: url,// mandatory
  data: data,
  success: success,
  dataType: dataType,
  async:false // to make it synchronous
});
Anupam
la source
1
pourquoi est-ce downvoted?
alias51
-2

Défini asyn:falsedans la requête ajax pour le rendre synchrone.

Voici l'exemple de code:

 $.ajax({
    url: 'some_url',
    type: "GET",
    async: false,
    cache: false,
    dataType: "JSON",
    data: { 
      "p1": v1, 
      "p2": v2
    },
    success: function(result) {
    }  
  }
});

Cela peut rendre l'écran insensible.

Usman Afzal
la source