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

StackOverflow https://stackoverflow.com/questions/133310

Question

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 renvoyer false dans la fonction mère, pas dans le rappel. Existe-t-il un moyen d'effectuer une requête AJAX synchrone à l'aide de jQuery ou de toute autre API intégrée au navigateur?

Était-ce utile?

La solution

Dans la documentation jQuery : vous spécifiez la asynchrone . L'option doit être false pour obtenir une demande Ajax synchrone. Ensuite, votre callback 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
    });
}

Autres conseils

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 (...);

.

Il suffit ensuite 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 son jQuery.get () ou jQuery.post ( ) vers la syntaxe plus élaborée jQuery.ajax () .

Excellente solution! Lorsque j'ai essayé de l'implémenter, j'ai remarqué que si je renvoyais une valeur dans la clause success, celle-ci revenait comme indéfinie. Je devais le stocker dans une variable et renvoyer cette variable. C’est la méthode que j’ai imaginé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;
}

Toutes ces réponses manquent le 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();
}
function getURL(url){
    return $.ajax({
        type: "GET",
        url: url,
        cache: false,
        async: false
    }).responseText;
}


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

N'oubliez pas que si vous passez un appel Ajax entre domaines (en utilisant JSONP ), vous < strong> ne peut pas le faire de manière synchrone, l'indicateur 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. Modifiez votre code pour qu'il fonctionne de manière asynchrone
  3. Utiliser un "séquenceur de fonctions" " bibliothèque comme Frame.js (cette answer )
  4. Bloquez l'interface utilisateur au lieu de bloquer l'exécution (cette answer ) (ma méthode préférée)

Remarque: vous ne devez pas utiliser async: false en raison de ces messages d'avertissement:

  

À 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 des effets négatifs 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 obtenir de l'aide supplémentaire, consultez https://xhr.spec.whatwg.org/ .

Cela pourrait casser votre page si vous agissez de la sorte, car elle pourrait ne plus fonctionner tous les jours.

Si vous voulez le faire de manière synchrone mais que vous ne bloquez pas, vous devez utiliser async / wait et probablement aussi un ajax basé sur des promesses comme le nouveau Récupérer l'API

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

Modifier chrome travaille sur la Refuser la synchronisation XHR dans le renvoi d'une page lorsque la page est explorée 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 consulter navigator.sendBeacon à la place

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

.

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 & amp; la co bibliothèque :

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

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 type de données de 'JSON' et modifié le .responseText en responseJSON .

>

J'ai également récupéré le statut à l'aide de 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.

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

J'ai modifié l'option GET to 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ù asynchrone 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, mais uniquement après la fin de l'appel asynchrone. beaucoup trop tard pour définir la réponse .

Voici un exemple:

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

Tout d'abord, 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 un contrôle de bas niveau sur la requête ajax. Uniquement pour obtenir / poster les données sur le serveur. Il s'agit d'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 opter pour $ .ajax

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

C’est ma simple implémentation pour les requêtes ASYNC avec jQuery. J'espère que cela aidera tout le monde.

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

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

Il est également transparent afin 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;
};

Ceci est mon & # 8220; fil de discussion & # 8221; espace de noms:

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 vous regardez la console, vous obtenez le résultat suivant:

start 1:41:21 PM
end 1:41:31 PM
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top