web-dev-qa-db-ja.com

ループ内のネストされたajax呼び出しで$ .Deferred()を使用する

私は似たような質問を探して解決策を試すのに非常に多くの時間を費やしてきたので、誰かが解決策を持っていることを願っています。

基本的に、関数a()が完了したときに通知を受け取りたいのですが、問題は、関数にajax呼び出しと、再びajaxを含むb()を呼び出すループが含まれていることです。コール。

FIDDLEで更新: http://jsfiddle.net/hsyj7/1/

そのようです:

// called by main()
function a() {
  return $.ajax("http://url1").pipe(function(data){
    for (var i = 0; i < 2; i++) {
      console.log('a called');
      b();
    }
  });
}

// called by a()
function b() {
  for (var i = 0; i < 2; i++) {
    $.ajax("http://url2", function(data){
      // do something
      console.log('b called');
    }
  }
}

function main(){
  $.when(a()).done(function(){
    console.log('all completed');
  });
}

それで私が見たいのは、おそらく上部にa()の両方の呼び出しがある:

a called
b called
b called
a called
b called
b called
all completed

代わりに私は得る

a called
all completed
b called
b called

またはそのいくつかの変形。

上記のコードには、ループとb()の両方で遅延機能が欠落していることを認識しています。私が試したいくつかのバリアントでは、main()のdone()ハンドラーが呼び出されることはありません。

誰かがこれを行う方法を知っていますか?

30
jokkedk

ええ、Deferredを使用するのがそれを行う方法です:

function a() {
    var def = $.Deferred();

    $.ajax("http://url1").done(function(data){
        var requests = [];

        for (var i = 0; i < 2; i++) {
             requests.Push(b());
        }

        $.when.apply($, requests).then(function() { def.resolve(); });
    });

    return def.promise();
}

// called by a()
function b() {
    var def = $.Deferred(),
        requests = [];

    for (var i = 0; i < 2; i++) {
        requests.Push($.ajax("http://url2").done(function(data){
            // do something
            console.log('b called');
        });
    }

    $.when.apply($, requests).then(function() { def.resolve(); });

    return def.promise();
}

function main(){
    $.when(a()).done(function(){
        console.log('all completed');
    });
}

//編集:置換.pipe.done

28
freakish

質問は古いかもしれませんが、正しい解決策がまだないので、ここに答えを置きます。 .then (以前は.pipeでした)を使用してpromiseを適切にチェーンし、要求された結果を達成します。

function a() {
  return $.ajax("http://url1").done(function(data){
    console.log('a called');
  }).then(function(){
    return $.when(b(), b()); // no loop for simplicity
  });
}
function b() {
  return $.ajax("http://url2").done(function(data){
    console.log('b called');
  });
}

function main(){
  a().done(function(){
    console.log('all completed');
  }, function() {
    console.log('an error occured!');
  });
}

ネスト/構造が変更される可能性のある場所で利用可能な結果データに応じて、全体的な順序は正しいです。

2
Bergi

より高いコンテキストにあるArrayを使用して、Promise/Deferredオブジェクトをプッシュすることができます。次に、jQuery.whenFunction.prototype.applyと一緒に使用して、すべてのエントリを引数として渡すことができます。

(function() {
    var promises = [ ],
        when = Function.prototype.apply.bind( jQuery.when, null );

    function a() {
         promises.Push($.ajax("http://url1").pipe(function(data){
             for (var i = 0; i < 2; i++) {
                 console.log('a called');
                 b();
             }
         }));

         return promises;
    }

    function b() {
        for (var i = 0; i < 2; i++) {
            promises.Push($.ajax("http://url2", function(data) {
                // do something
                console.log('b called');
            }));
        }
    }

    function main() {
        promises = [ ];

        when( a() ).done(function(){
            console.log('all completed');
        });
    }
}());
2
jAndy

これはコールバックで修正できると思いますが、フィドルは私があなたをチェックするのに本当に役立ちました。

 // called by main()
 function a(callback) {
   //set this to the number of loops that is going to happen
   var number = 2;
   return $.ajax("http://url1", function(data){
     console.log('a called');
     for (var i = 0; i < number ; i++) {
       b();
       if(number===i){
           callback();
       }
     }
   }
 }

 function main(){
    a(function(){
       //Function or code you want to run on completion.. 
    });
 }

これがうまくいかない場合は許してください、しかし私はそれが正しい方向だと思います。

1
Jamie Hutber