web-dev-qa-db-ja.com

jQuery Promiseを使用して3つの非同期呼び出しをチェーンするにはどうすればよいですか?

同期方式で行う必要がある3つのHTTP呼び出しがあり、1つの呼び出しから別の呼び出しにデータを渡す方法を教えてください。

function first()
{
   ajax()
}

function second()
{
   ajax()
}

function third()
{
   ajax()
}


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

2つの関数に遅延を使用しようとしましたが、部分的な解決策を思い付きました。 3つの機能のために拡張できますか?

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

いずれの場合も、$.ajax()によって返されるjqXHRオブジェクトを返します。

これらのオブジェクトはPromiseと互換性があるため、.then()/.done()/.fail()/.always()とチェーン化できます。

.then()は、質問の場合とまったく同じように、この場合に必要なものです。

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

引数datatextStatusおよびjqXHRは、前の関数の$.ajax()呼び出しから発生します。 first()フィードsecond()およびsecond()フィードthird()

DEMO$.when('foo')の代わりに、履行された約束を果たすために$.ajax(...)を使用) 。

80

JQueryでpromiseを使用する場合、実際にははるかに簡単なアプローチがあります。以下をご覧ください。

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

すべての呼び出しを$ .when(...)呼び出しにチェーンし、.done(...)呼び出しで戻り値を処理するだけです。

ご希望の場合のウォークスルーは次のとおりです。 http://collaboradev.com/2014/01/27/understanding-javascript-promises-in-jquery/

34
user3242460

返事はかなり遅れていますが、答えには連鎖のための簡単なコードが欠けていると思います。 jqueryでpromiseがサポートされているため、イベントのチェーンは非常に簡単です。チェーンには次のものを使用します。

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

それは単純で、複雑なforループや複数のネストされたコールバックはありません。

23
prajnavantha

それよりもずっと簡単です。

$.ajaxはすでにpromise(遅延オブジェクト)を返しますので、次のように書くことができます。

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

より機能的な方法でそれを書くことができます:

[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

ここで見栄えの良い解決策を見つけました: jQuery 1.8.xで一連の遅延関数を連鎖させるにはどうすればよいですか?

そして、これは私自身の同様のアプローチの実装で、ややいですが、おそらく動作しています。返されたpromiseオブジェクトで"progress update"として各メソッドの結果をブロードキャストします。

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

状況に合わせてどのように使用しますか?たぶんきれいではないかもしれませんが、うまくいくはずです:

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

または、first関数からそのid変数を割り当てることができます。

または、前の関数の結果のみが必要な場合は、このアプローチを使用できます。

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

以下は機能しているように見え、関数のリストを動的にすることができます。

<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

これを行う最良の方法は、これのために再利用可能な関数を作成することです。これは、reduceを使用する1行のコードでも実行できます。

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

この関数は、3つの関数のように、promiseオブジェクトを返すコールバックの配列を受け入れます。

使用例:

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

このようにfirstの結果も自動的にsecondのパラメーターになるため、基本的には次のようになります。

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

そしてもちろん、必要なだけ関数を配列に追加できます。

1
Duncan Luk