2017-10-14 6 views
0

J'ai plusieurs fonctions Javascript, chacune effectue une manipulation DOM, puis exécute une requête ajax. Je veux être capable d'exécuter la première fonction, qui manipule le DOM puis déclenche sa requête ajax, puis, lorsque la requête ajax se termine, je veux exécuter la deuxième fonction si la requête ajax est vraie, ou arrêter l'exécution du reste des fonctions et effectuer d'autres manipulations DOM (comme afficher un message d'erreur). Je souhaite que ces fonctions s'exécutent séquentiellement, l'une après l'autre, et ne se poursuivent que si aucune d'entre elles ne renvoie false à partir de leur requête ajax. Si aucun d'entre eux ne retourne faux, alors tous devraient fonctionner et finalement je ferais une manipulation que j'ai dans mon callback "toujours".Comment puis-je exécuter plusieurs fonctions de manière séquentielle et arrêter toutes les opérations en cas d'échec?

Comment puis-je accomplir ceci? Ma première pensée a été d'utiliser des promesses, pour garder le code propre, mais après plusieurs heures de lecture, je n'arrive tout simplement pas à comprendre comment faire fonctionner ce code.

Ci-dessous mon code actuel, et c'est ce que je reçois dans ma console quand il exécute:

inside test1 
inside test2 
inside test3 
fail 
[arguments variable from fail method] 
always 
[arguments variable from always method] 

Voici ce que je veux dans ma console (notez la contient pas de chaîne « à l'intérieur test3 ») :

inside test1 
inside test2 
fail 
[arguments variable from fail method] 
always 
[arguments variable from always method] 

Voici le code:

(function($) { 
    var tasks = { 
     init: function() { 
      $.when(
       this.test1(), 
       this.test2(), 
       this.test3() 
      ).done(function() { 
       console.log('done'); 
       console.log(arguments); 
      }) 
      .fail(function() { 
       console.log('fail'); 
       console.log(arguments); 
      }) 
      .always(function() { 
       console.log('always'); 
       console.log(arguments); 
      }); 
     }, 

     test1: function() { 
      console.log('inside test1'); 
      return $.getJSON('https://baconipsum.com/api/?type=meat-and-filler'); 
     }, 

     test2: function() { 
      console.log('inside test2'); 
      // note the misspelled "typ" arg to make it fail and stop execution of test3() 
      return $.getJSON('https://baconipsum.com/api/?typ=meat-and-filler'); 
     }, 

     test3: function() { 
      console.log('inside test3'); 
      return $.getJSON('https://baconipsum.com/api/?type=meat-and-filler'); 
     } 
    }; 

    tasks.init(); 
})(jQuery) 

Toutes les idées?

Répondre

1

En utilisant les promesses ne fait ce code beaucoup plus propre

Note: Promesse/A + promesse callbacks .alors et .catch ne jamais prendre un argument, donc pas besoin de regarder à arguments, il suffit d'utiliser un seul argument

Ce code (ES2015 +) montre la facilité

let p = Promise.resolve(); 
Promise.all([this.test1, this.test2, this.test3].map(fn => p = p.then(fn()))).then(allResults => 

alternativement, en utilisant array.reduce

[this.fn1, this.fn2, this.fn3].reduce((promise, fn) => promise.then(results => fn().then(result => results.concat(result))), Promise.resolve([])).then(allResults => 

Dans les deux cas, allResults est un tableau de (résolu) résulte de la testN fonctions

Il crée un (résolu) promesse de « commencer » la chaîne des promesses

le mappage # mappe chaque fonction (this.test1 etc) à exécuter dans .then du résultat précédent.Les résultats de chaque this.testn promesse sont retournés dans un nouveau tableau, qui est l'argument pour Promise.all

si l'un des TestN échouent, la prochaine ne sera pas en exécution

var tasks = { 
    init() { 
     let p = Promise.resolve(); 
     Promise.all([this.test1, this.test2, this.test3].map(fn => p = p.then(fn()))) 
     .then(results => { 
      console.log('done'); 
      console.log(results); 
      return results; // pass results to next .then 
     }).catch(reason => { 
      console.log('fail'); 
      console.log(reason); 
      return reason; // because I return rather than throw (or return a Promise.reject), 
      //the next .then can will get `reason` in it's argument 
     }).then(result => { 
      console.log('always'); 
      console.log(result); 
     }); 
    }, 
    test1() { 
     return $.getJSON('https://baconipsum.com/api/?type=meat-and-filler'); 
    }, 
    test2() { 
     return $.getJSON('https://baconipsum.com/api/?typ=meat-and-filler'); 
    }, 
    test3() { 
     return $.getJSON('https://baconipsum.com/api/?type=meat-and-filler').then(result => { 
      if(someCondition) { 
       throw new Error("sum ting wong"); 
       // or 
       return Promise.reject(new Error("sum ting wong")); 
      } 
      return result; 
     }); 
    } 
}; 
tasks.init(); 

pour le code dans la question, vous pourriez simplifier encore

var tasks = { 
    init() { 
     let p = Promise.resolve(); 
     const urls = [ 
      'https://baconipsum.com/api/?type=meat-and-filler', 
      'https://baconipsum.com/api/?typ=meat-and-filler', 
      'https://baconipsum.com/api/?type=meat-and-filler' 
     ]; 
     Promise.all(urls.map(url => p = p.then(() => $.getJSON(url)))) 
     .then(results => { 
      console.log('done'); 
      console.log(results); 
      return results; // pass results to next .then 
     }).catch(reason => { 
      console.log('fail'); 
      console.log(reason); 
      return reason; // because I return rather than throw (or return a Promise.reject), 
      //the next .then can will get `reason` in it's argument 
     }).then(result => { 
      console.log('always'); 
      console.log(result); 
     }); 
    } 
}; 
tasks.init(); 
+0

ressemble à ce que j'étais après ... bien que je ne sois pas sûr si les fonctions de grosse flèche sont soutenues de la même manière que les fonctions anonymes régulières sont? Je vais essayer, mais ça a l'air vraiment génial! Merci, @Jaromanda X – andrux

+0

'=>' rend le code plus concis, et moins de tracas avec quoi 'ceci' est: p vous pouvez toujours transpiler le ci-dessus –

+0

J'ai converti votre code en JS" régulier ", il s'exécute mais il doesn ' t enregistrer ce que je m'attendais dans la console: https://gist.github.com/andruxnet/11a0c2baa45e870fc449bac02cb66bf5 – andrux

0

Vous n'avez pas besoin d'utiliser des promesses - et peut-être vous le savez, mais vous pouvez continuer à appeler la prochaine demande ajax dans le bloc un des succès prevoius, comme ceci:

(function($) { 
    var tasks = { 
     init: function() { 
        $.post(url_1, data_1, function(data,status){ 
      if(status == 'success') 
      { 
       $.post(url_2, data_2, function(data,status){ 
        if(status == 'success') 
        { 
         // ... and the next Ajax request goes here, etc. 
        } 
        else { 
         // show error message if 2nd Ajax request fails 
        } 
       } 
      else { 
       // show error message if 1st Ajax request failes 
      } 
     }); 
    });  
     } 

    tasks.init(); 
})(jQuery) 
+0

bienvenue à la pyramide de malheur ou de l'enfer de rappel - imaginez même à seulement 10 urls: p –

+0

en fait, le code de la question est plus courte que ce que j'ai en fait, le code réel a environ 8 demandes ajax, et cela 's possible d'ajouter encore plus – andrux

1

Je ne suis pas comme familier avec les promesses jquery.

Mais cela devrait fonctionner

(function ($) { 
    var tasks = { 
     init: function() { 
      this.test1().done(this.test2).done(this.test3) 
       .done(function() { 
        console.log('done'); 
        console.log(arguments); 
       }) 
       .fail(function() { 
        console.log('fail'); 
        console.log(arguments); 
       }) 
       .always(function() { 
        console.log('always'); 
        console.log(arguments); 
       }); 
     }, 

     test1: function() { 
      console.log('inside test1'); 
      return $.getJSON('https://baconipsum.com/api/?type=meat-and-filler'); 
     }, 

     test2: function() { 
      console.log('inside test2'); 
      // note the misspelled "typ" arg to make it fail and stop execution of test3() 
      return $.getJSON('https://baconipsum.com/api/?typ=meat-and-filler'); 
     }, 

     test3: function() { 
      console.log('inside test3'); 
      return $.getJSON('https://baconipsum.com/api/?type=meat-and-filler'); 
     } 
    }; 

    tasks.init(); 
})(jQuery) 
+0

Merci, je suppose que cela peut fonctionner, et je voudrais l'utiliser si je ne pense pas qu'il est possible d'ajouter plus de demandes ajax, donc je veux que le code soit facilement extensible .. cette solution Je devrais être un peu brouillon avec plusieurs autres rappels – andrux

+0

Je ne sais pas exactement ce que vous voulez dire, cela enchaîne les 3 promesses que vous étiez en train de créer, ce qui est ce que vous demandiez. Je suppose que dans votre code actuel, vous faites plus à l'intérieur des fonctions test1, test2, test3 en plus de rechercher les données json. Quels autres changements recherchiez-vous? –

+0

Je veux dire, ça marche (ou du moins ça devrait marcher comme je peux le voir) mais je veux pouvoir ajouter facilement plus de fonctions, enchaînant test1, test2, ..., test20 peut sembler un peu brouillon et plus difficile à maintenir (Et si je voulais en commenter certaines?) C'est une bonne solution, mais ce code pourrait être maintenu dans le futur par d'autres développeurs et je veux le rendre aussi facile que possible pour eux si je ne suis pas là – andrux