2
votes

jQuery AJAX relation entre succès et .then

J'essaie de résoudre un problème très simple, mais je n'arrive pas à le comprendre. J'ai le code suivant:

<button onclick="run()">TEST</button>
<script>
    var counter = 0;
    function test() {           
        return $.ajax({
           url: "https://127.0.0.1:4432",
           success: function(html){
              console.log('success! test');
           },
           error: function() {
            if (counter++ < 3) {
                console.log("Retrying");
                $.ajax(this);
                return;
            }           
            console.log('ERROR! test');
           }
        });
    }   

    function run() {
        test()
        .then(
            function(){console.log('finished ALL OK')}, 
            function() {console.log('finished all ERROR')
        ;});

    }


</script>

Ce que je veux, c'est qu'après avoir cliqué sur le bouton, la requête AJAX sera envoyée au serveur. En cas d'échec de l'AJAX, la demande sera réessayée. Ce n'est qu'après que toutes les tentatives seront terminées, alors le succès ou l'échec de la fonction ".then" sera appelé.

À ce moment, le code se comporte comme si l'appel AJAX est retenté trois fois, mais le Le gestionnaire ".then ()" est appelé juste après le premier échec.

Dans la console du navigateur, il ressemble à ceci:

 Console du navigateur

Je voudrais voir le texte "terminé toutes les ERREURS" à la fin après toutes les tentatives.

J'espère que cela a du sens et je serai reconnaissant pour toute aide.


1 commentaires

La fonction .then () est appelée lorsque la requête AJAX se termine, qu'elle réussisse ou échoue. S'il réussit, le premier rappel est appelé, s'il échoue, le second est appelé. Ceci est indépendant de l'appel des rappels success ou error .


4 Réponses :


0
votes

function test(counter, $existingDeferred) {
        var $deferred = $existingDeferred || $.Deferred();
        
        $.ajax({
           url: "https://127.0.0.1:4432",
           success: function(html){
              console.log('success! test');
              $deferred.resolve(arguments);
           },
           error: function() {
            console.log('ERROR! test');
            
            if (counter < 3) {
                console.log("Retrying");
                test(++counter, $deferred);
            } else {
                $deferred.reject(arguments);
            }
          }
        });
        
        return $deferred;
    }   

    function run() {
        test(0)
        .then(
            function(){console.log('finished ALL OK')}, 
            function() {console.log('finished all ERROR')
        ;});

    }

Vous pouvez créer un différé secondaire qui contrôle le moment où les tentatives de relance sont terminées.

  • Transmettez le décompte du compteur et éventuellement le secondaire différé
  • Si nous n'avons pas de différé existant, créez-en un.
    • Exécutez la requête ajax
    • En cas de succès, résolvez le différé
    • En cas d'erreur
    • Si les tentatives n'ont pas été expirées, réexécutez la méthode avec le compteur incrémenté et l'existant différé.
    • Sinon, rejetez le report pour signifier la fin du processus
  • Renvoie la valeur différée qui sera résolue en cas de succès ou rejetée lorsque toutes les tentatives seront épuisées.


0 commentaires

1
votes

Ce qui se trouve dans la méthode success n'affectera pas le .then sur le même objet jqXHR. Pour le chaînage que vous recherchez, envisagez d'utiliser uniquement .puis s et .catch es à la place:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
function test() {
  let counter = 0;
  const get = () => $.ajax({ url: "https://127.0.0.1:4432" })
  .then((html) => {
    console.log('success');
  })
  .catch((err) => {
    if (counter++ < 3) {
      console.log("Retrying");
      return get();
    }
    console.log('ERROR! test');
    throw new Error();
  });
  return get();
}

test()
  .then(
  function() {
    console.log('finished ALL OK')
  },
  function() {
    console.log('finished all ERROR');
  });


3 commentaires

propre et efficace.


Merci, c'est la bonne solution pour moi. Je n'ai eu qu'à remplacer les fonctions fléchées par des fonctions normales et je ne peux pas utiliser la méthode .catch car je dois utiliser jquery 1.9 et il doit fonctionner dans IE11. Alors merci encore


Si votre script est de taille raisonnable, je vous recommande vivement d'intégrer Babel et les polyfills dans votre processus de construction afin que vous puissiez écrire, lire et déboguer le code dans la dernière et la meilleure version du langage, et le transpiler vers ES5 pour production afin que les anciens navigateurs puissent le comprendre.



-1
votes

une solution possible consiste à envelopper l'ensemble de votre opération dans un Promise , bien que vous ayez peut-être besoin d'un polyfill pour une prise en charge à 100% du navigateur. qui ressemblerait à ceci:

function test() {
    return new Promise(function(resolve, reject) {
        var counter = 0;
        $.ajax({
            url: "https://127.0.0.1:4432",
            success: function(html){
                console.log('success! test');
                resolve(html);
            },
            error: function() {
                if (counter++ < 3) {
                    console.log("Retrying");
                    $.ajax(this);
                }
                console.log('ERROR! test');
                reject('retries exceeded');
            }
        });
    });
}

function run() {
    test().then(
        function() { console.log('finished ALL OK'); },
        function() { console.log('finished all ERROR'); }
    );
}


3 commentaires

La seule raison pour laquelle vous envelopperiez quelque chose qui renvoie déjà une promesse dans une autre promesse est si la promesse qu'elle renvoie n'est pas suffisamment conforme aux normes pour servir l'objectif. Sauf si vous utilisez une très ancienne version de jquery, ce n'est pas le cas ici.


pour être honnête, les très anciennes versions de jquery sont encore assez courantes


problème que j'ai avec stackoverflow en général; il est difficile de savoir quelle est la cible, donc je me retrouve le plus souvent à revenir sur une méthodologie plus ancienne dans les réponses. votre critique sur l'utilisation appropriée des promesses supplémentaires est cependant valable et bien accueillie.



0
votes

Voici une autre option utilisant la récursivité.

function test(fn, retries) {
  return fn().catch(error => 
    retries > 0 ? test(fn, retries - 1) : Promise.reject(error)
  );
}

test(() => $.ajax({ url: "https://127.0.0.1:4432" }), 3)
  .then(data => console.log('finished ALL OK'))
  .catch(lastError => console.log('finished all ERROR'));

Si fn est appelé avec une réponse réussie, le catch n'est jamais appelé arrêt de la récursivité. Si fn n'a pas de réponse positive, il vérifie s'il reste des tentatives. Si tel est le cas, il appelle à nouveau test avec une nouvelle tentative de moins, s'il n'y a plus de tentatives, Promise.reject est appelé en fournissant la dernière erreur.

Remarque: Si vous n'utilisez pas jQuery version 3.0 ou supérieure, vous devrez remplacer .catch (...) par . puis (null, ...) .


0 commentaires