web-dev-qa-db-ja.com

同期および非同期関数の順次実行のためのjQueryDeferredおよびPromise

同期関数と非同期関数を特定の順序で実行したい場合は、jQuery promiseを使用できますが、期待どおりに機能しないようです。

関数a、b、cは、deferred.resolve()が呼び出されたときにこの順序で実行される必要があります。関数bが実行されることを期待しますが、resolveが呼び出されても、すべての関数はすぐに実行されます。

コードは次のとおりです。

function a(){
  var deferred = $.Deferred();
  setTimeout(function(){
    console.log("status in a:",deferred.state());
    //this should trigger calling a or not?
    deferred.resolve("from a");
  },200);
  console.log("a");
  return deferred.promise();
};
function b(){
  var deferred = $.Deferred();
  setTimeout(function(){
    console.log("status in b:",deferred.state());
    deferred.resolve("from b");
  },200);
  console.log("b");
  return deferred.promise();
}
//synchronous function
function c(){
  var deferred = $.Deferred();
  console.log("c");
  console.log("status in c:",deferred.state());
  deferred.resolve("from c");
  return deferred.promise();
}
function test(){
  fn=[a,b,c],i=-1,
  len = fn.length,d,
  d = jQuery.Deferred(),
  p=d.promise();
  while(++i<len){
    p=p.then(fn[i]);
  }
  p.then(function(){
    console.log("done");
  },
  function(){
    console.log("Failed");
  });
  d.resolve();
  //instead of the loop doing the following has the same output
  //p.then(a).then(b).then(c);
  //d.resolve();
}
test();

出力は次のとおりです。

a
b
status in c: pending
c
done
status in a: pending
status in b: pending

期待される出力:

a
status in a: pending
b
status in b: pending
c
status in c: pending
done

次の変更のいくつかの組み合わせを試しました。

  d = jQuery.Deferred();
  setTimeout(function(){d.resolve();},100);
  var p=d.promise();
  while(++i<len){
    p.then(fn[i]);
  }

しかし、すべて同じ予期しない結果で、bはaの延期が解決される前に呼び出され、cはbの延期が解決される前に呼び出されます。

13
HMR

1.8より前のjQueryの場合、これは問題ですが、jQueryの新しいバージョンの場合、これはもう問題ではありません。

function test(){
  var d = jQuery.Deferred(), 
  p=d.promise();
  //You can chain jQuery promises using .then
  p.then(a).then(b).then(c);
  d.resolve();
}
test();

[〜#〜]デモ[〜#〜]

以下はjQuery1.7.2のデモです

[〜#〜]デモ[〜#〜]

9
Khanh TO

jQuery <1.8は優れたWRTチェーンであり、.pipeの代わりに.thenを使用するだけです。 1.8は単に.then.pipeに変更しました。

2
Esailija

補足:配列なしで使用する場合、promiseから始める必要はありません。 $.when({}).then(a).then(b)はうまく機能します。 awhenの中に入れないようにする必要があるだけです。

1
Bet Lamed