web-dev-qa-db-fra.com

Comment chaîner trois appels asynchrones à l'aide des promesses jQuery?

J'ai trois appels HTTP que je dois effectuer de manière synchrone et comment puis-je transmettre les données d'un appel à l'autre?

function first()
{
   ajax()
}

function second()
{
   ajax()
}

function third()
{
   ajax()
}


function main()
{
    first().then(second).then(third)
}

J'ai essayé d'utiliser le différé pour les deux fonctions et j'ai proposé une solution partielle. Puis-je l'étendre à trois fonctions?

function first() {
    var deferred = $.Deferred();
     $.ajax({

             "success": function (resp)
             {

                 deferred.resolve(resp);
             },

         });
    return deferred.promise();
}

function second(foo) {
     $.ajax({
            "success": function (resp)
            {
            },
            "error": function (resp)
            {
            }
        });
}


first().then(function(foo){second(foo)})
65
John Mcdock

Dans chaque cas, retourne l'objet jqXHR renvoyé par $.ajax().

Ces objets sont compatibles avec Promise et peuvent donc être chaînés avec .then()/.done()/.fail()/.always().

.then() est celui que vous voulez dans ce cas, exactement comme dans la question.

function first() {
   return $.ajax(...);
}

function second(data, textStatus, jqXHR) {
   return $.ajax(...);
}

function third(data, textStatus, jqXHR) {
   return $.ajax(...);
}

function main() {
    first().then(second).then(third);
}

Les arguments data, textStatus et jqXHR découlent de l'appel $.ajax() de la fonction précédente, c'est-à-dire. first() alimente second() et second() alimente third().

DÉMO (avec $.when('foo') pour tenir une promesse remplie , à la place de $.ajax(...)).

80
Beetroot-Beetroot

Il existe en fait une approche beaucoup plus simple lorsque vous utilisez les promesses avec jQuery. Regardez ce qui suit:

$.when(
    $.ajax("/first/call"),
    $.ajax("/second/call"),
    $.ajax("/third/call")
    )
    .done(function(first_call, second_call, third_call){
        //do something
    })
    .fail(function(){
        //handle errors
    });

Il vous suffit de chaîner tous vos appels dans l'appel $ .when (...) et de gérer les valeurs de retour dans l'appel .done (...).

Voici une procédure si vous préférez: http://collaboradev.com/2014/01/27/understanding-javascript-promises-in-jquery/

34
user3242460

Assez tard pour répondre, mais j'imagine qu'il manque un code simple pour chaîner. Le chaînage d'événements est assez simple avec un support de promesse dans jQuery. J'utilise les éléments suivants pour chaîner:

$.ajax()
.then(function(){
   return $.ajax() //second ajax call
})
.then(function(){
   return $.ajax() //third ajax call
})
.done(function(resp){
   //handle final response here
 })

C'est simple, sans boucles for complexes ou plusieurs rappels imbriqués.

23
prajnavantha

C'est beaucoup plus simple que ça.

$.ajax renvoie déjà une promesse (objet différé), vous pouvez donc simplement écrire

function first() {
    return $.ajax(...);
}
14
SLaks

Vous pouvez l'écrire de manière plus fonctionnelle:

[function() { return ajax(...)}, function(data) { return ajax(...)}]
.reduce(function(chain, callback) { 
  if(chain) { 
    return chain.then(function(data) { return callback(data); });
  } else {
    return callback();
  }
}, null)
6
Mike Vitik

J'ai trouvé une bonne solution ici: Comment chaîner une séquence de fonctions différées dans jQuery 1.8.x?

Et voici ma propre mise en œuvre d'une approche similaire, un peu laide mais probablement efficace. Il diffuse le résultat de chaque méthode en tant que "mise à jour de la progression" sur l'objet de promesse retourné.

  $.chain = function() {
      var defer = $.Deferred();
      var funcs = arguments;
      var left = funcs.length;
      function next(lastResult) {
          if(left == 0) {
              defer.resolve();
              return;
          }
          var func = funcs[funcs.length - left]; // current func
          var prom = func(lastResult).promise(); // for promise will return itself,
                                       // for jquery ojbect will return promise.
          // these handlers will be launched in order we specify them
          prom.always(function() {
              left--;
          }).done(function(ret) {
              defer.notify({
                  idx: funcs.length-left,
                  left: left,
                  result: ret,
                  success: true,
              });
          }).fail(function(ret) {
              defer.notify({
                  idx: funcs.length-left,
                  left: left,
                  result: ret,
                  success: false,
              });
          }).always(function(ret) {
              next(ret);
          });
      }
      next();
      return defer.promise();
  };

Comment l'utiliser pour votre situation? Peut-être pas beau, mais ça devrait marcher:

function first() {
    return ajax(...);
}

var id;

funciton second() {
    return ajax(id, ...);
}

function third() {
    return ajax(id, ...);
}

$.chain(first, second, third).progress(function(p) {
    if(p.func == first)
        id = p.result.identifier;
}).then(function() {
    alert('everything is done');
});

Ou vous pouvez simplement assigner cette variable id à partir de la fonction first.

Ou si vous n'avez besoin que du résultat de la fonction précédente, vous pouvez utiliser cette approche:

function first() {
    return ajax(...);
}
function second(first_ret) {
    return ajax(first_ret.id, ...);
}
function third(second_ret) {
    return ajax(second_ret.something, ...);
}
4
MarSoft

Ce qui suit semble fonctionner et permet à la liste de fonctions d’être dynamique:

<html>
  <head>
  <title>demo chained synchronous calls</title>
  </head>
  <body>

  <script src="http://code.jquery.com/jquery-2.2.4.min.js"></script>
  <script type="text/javascript">
    function one(parms) {
        console.log('func one ' + parms);
        return 1;
    }

    function two(parms) {
        console.log('func two ' + parms);
        return 2;
    }

    function three(parms) {
        console.log('func three ' + parms);
        return 3;
    }

    function four(parms) {
        console.log('func four ' + parms);
        return 4;
    }

    var funcs = ['one', 'two', 'three', 'four'];
    var rvals = [0];

    function call_next_func() {
        if (funcs.length == 0) {
            console.log('done');
        } else {
            var funcname = funcs.shift();
            console.log(funcname);
            rvals.Push(window[funcname](rvals));
            call_next_func();
        }
    }

    $(document).ready(function($){
        call_next_func();
    });
  </script>

  </body>
</html>
1
duanev

La meilleure façon de faire est de créer une fonction réutilisable pour cela. Cela peut même être fait avec une seule ligne de code en utilisant reduce:

function chainPromises(list) {
    return list.reduce((chain, func) => chain ? chain.then(func) : func(), null);
}

Cette fonction accepte un tableau de rappels qui renvoie un objet de promesse, comme vos trois fonctions.

Exemple d'utilisation:

chainPromises([first, second, third]).then(function (result) {
    console.log('All done! ', result);
});

De cette façon, le résultat de first sera aussi automatiquement le paramètre de second, donc voici ce qui se passe:

first().then(function(res1) { return second(res1) })
       .then(function(res2) { return third(res2)  })
       .then(function(result) { console.log('All done! ', result) });

Et bien sûr, vous pouvez ajouter autant de fonctions que vous le souhaitez au tableau.

1
Duncan Luk