web-dev-qa-db-fra.com

Comment faire en sorte que jQuery exécute une requête Ajax synchrone plutôt qu'asynchrone?

J'ai un widget JavaScript qui fournit des points d'extension standard. L'un d'eux est la fonction beforecreate. Il doit renvoyer false pour empêcher la création d'un élément.

J'ai ajouté un appel Ajax à cette fonction à l'aide de 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 retourner false dans 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 intégrée au navigateur?

1161
Artem Tikhomirov

A partir de la documentation jQuery : vous spécifiez l’option asynchrone comme étant false pour obtenir une requête Ajax synchrone. Ensuite, votre rappel peut définir certaines données avant que votre fonction mère ne 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
    });
}
1120
Adam Bellaire

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

jQuery.ajaxSetup({async:false});

Et effectuez ensuite vos appels Ajax avec jQuery.get( ... );

Puis il suffit de le rallumer une fois

jQuery.ajaxSetup({async:true});

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

250
Sydwell

Excellente solution! Lorsque j'ai essayé de l'implémenter, j'ai remarqué que si je retournais une valeur dans la clause de succès, elle revenait comme indéfinie. Je devais le stocker dans une variable et renvoyer cette variable. Voici la méthode que j'ai proposé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;
}
133
James in Indy

Toutes ces réponses passent à côté du fait que faire un appel Ajax avec async: false provoquera le blocage du navigateur jusqu'à la fin de la demande Ajax. 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();
}
83
BishopZ
function getURL(url){
    return $.ajax({
        type: "GET",
        url: url,
        cache: false,
        async: false
    }).responseText;
}


//example use
var msg=getURL("message.php");
alert(msg);
49
Carcione

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

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

Pour les appels JSONP, vous pouvez utiliser:

  1. Appelez Ajax vers votre propre domaine - et effectuez l'appel inter-domaine côté serveur
  2. Modifier votre code pour qu'il fonctionne de manière asynchrone
  3. Utilisez une bibliothèque "séquenceur de fonctions" comme Frame.js (this answer )
  4. Bloquez l'interface utilisateur au lieu de bloquer l'exécution (this answer ) (ma méthode préférée)
24
Serge Shultz

Remarque: vous ne devez pas utiliser async: false à cause des messages d’avertissement suivants:

À partir de 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 de leurs incidences négatives sur l'expérience utilisateur.

Chrome met même en garde à 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, vérifiez https://xhr.spec.whatwg.org/ .

Cela pourrait casser votre page si vous faites quelque chose comme ça, car cela pourrait cesser de fonctionner tous les jours.

Si vous voulez le faire de manière toujours synchrone mais que vous ne bloquez pas, vous devez utiliser async/wait et probablement aussi quelques ajax basés sur des promesses telles que 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 interdiction de sync XHR lors du retrait d'une page lorsque la page est en cours de navigation ou fermé 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 oeil à navigator.sendBeacon à la place

Il est également possible que la page désactive la synchronisation avec les en-têtes http ou l'attribut allow d'iframe.

24
Endless

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 renvoyé. Notez qu'il s'agit du statut de la réponse Ajax et non de la validité du JSON.

Le back-end doit renvoyer la réponse dans un JSON correct (bien formé), sinon l'objet renvoyé sera indéfini.

Lorsque vous répondez à la question initiale, vous devez prendre en compte deux aspects. L'un 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 aussi essayé avec POST et cela a fonctionné.

J'ai changé le GET en POST et ajouté data: 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ù asynchrone est false. Si vous deviez définir async: true l'objet renvoyé jqxhr ne serait pas valide au moment du retour de l'appel AJAX, mais ultérieurement uniquement lorsque l'appel asynchrone a terminé, mais il est beaucoup trop tard pour définir la variable réponse.

11
paulo62

Avec async: false vous 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 wait:

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

C'est exemple:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});
6
searching9x

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

Lorsque nous demandons un contrôle de bas niveau sur la requête ajax, tels que les paramètres d'en-tête de requête, les paramètres de mise en cache, les paramètres synchrones, etc., nous devrions alors choisir $ .ajax.

$ .get/$. post: Lorsque nous n'exigeons pas de contrôle de bas niveau sur la requête ajax.Only get/post not the data to the server .C'est un raccourci de

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

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

Par conséquent, pour le contrôle de bas niveau (synchronisation, cache, etc.) sur une requête ajax, nous devrions utiliser $ .ajax

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

Étant donné que XMLHttpReponse opération synchrone est obsolète, j'ai proposé la solution suivante qui encapsule XMLHttpRequest. Cela autorise les requêtes ordonnées AJAX tout en restant asynchrones, ce qui est très utile pour les jetons CSRF à usage unique.

Il est également transparent pour que des 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;
};
1
Geoffrey

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

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();
1
Felipe Marques

Ceci est mon espace de noms “Thread”:

var Thread = {
    sleep: function(ms) {
        var start = Date.now();

        while (true) {
            var clock = (Date.now() - start);
            if (clock >= ms) break;
        }

    }
};

Et voici comment je l'appelle:

var d1 = new Date();
console.log('start ' + d1.toLocaleTimeString());
Thread.sleep(10000);
var d2 = new Date();
console.log('end ' + d2.toLocaleTimeString());

Et si on regarde la console on obtient le résultat suivant:

start 1:41:21 PM
end 1:41:31 PM
0
Tell Me How