$ .when.apply($、someArray)は何をしますか?


110

私はDeferredsとPromisesについて読んでいて、出会い続けてい$.when.apply($, someArray)ます。私はこれが正確に何をするかについて少し不明確です、1行が正確に機能する(コードスニペット全体ではなく)との説明を探しています。ここにいくつかのコンテキストがあります:

var data = [1,2,3,4]; // the ids coming back from serviceA
var processItemsDeferred = [];

for(var i = 0; i < data.length; i++){
  processItemsDeferred.push(processItem(data[i]));
}

$.when.apply($, processItemsDeferred).then(everythingDone); 

function processItem(data) {
  var dfd = $.Deferred();
  console.log('called processItem');

  //in the real world, this would probably make an AJAX call.
  setTimeout(function() { dfd.resolve() }, 2000);    

  return dfd.promise();
}

function everythingDone(){
  console.log('processed all items');
}

1
.done()代わりに使用することができる.thenだけFYI、この場合は
ケビン・B

2
fwiw、アンダースコアへの遅延ポートがあり、単一の配列を渡すことができる_.whenため、使用する必要がありませんapply
Eevee



1
OPが最初の文で参照している記事は場所を移動しました。現在はflaviocopes.com/blog/deferreds-and-promises-in-javascriptにあります。
glaucon

回答:


161

.apply引数の配列で関数を呼び出すために使用されます。配列の各要素を取り、それぞれを関数のパラメーターとして使用します。 関数内.applyのコンテキスト(this)を変更することもできます。

それでは、取りましょう$.when。「これらの約束がすべて解決されたら...何かをする」と言うのに使われます。無限(可変)数のパラメーターを受け取ります。

あなたの場合、あなたは約束の配列を持っています。渡すパラメーターの数がわかりません$.when。配列自体を渡すこと$.whenは機能しません。これは、そのパラメータが配列ではなく、promiseであると想定しているためです。

それが.apply出番です。それは配列を取り、$.when各要素をパラメーターとして呼び出します(そして、thisjQuery/に設定されていることを確認します$)。


3
複数のpromiseが$ .whenメソッドに渡されたとき。どのような順序で実行されますか?次々または並行して?
Darshan 2013年

21
@ダーシャン:あなたは約束を「実行」しない。あなたはそれらが解決されるのを待ちます。これらは作成時に実行され、$.whenすべてが完了するのを待ってから続行します。
Rocket Hazmat 2013

1
何の違いについて $.when($, arrayOfPromises).done(...)$.when(null, arrayOfPromises).done(...) (...私は、フォーラムで提案されたソリューションの両方を発見した)
zeroquaranta

63

$ .whenは任意の数のパラメーターを取り、これらすべてが解決されると解決ます。

anyFunction .apply(thisValue、arrayParameters)は関数呼び出しanyFunctionそのコンテキストを設定する(thisValueがあろう。このこと関数呼び出し中)を、個々のパラメータとしてarrayParameters内のすべてのオブジェクトを渡します。

例えば:

$.when.apply($, [def1, def2])

と同じです:

$.when(def1, def2)

しかし、applyの呼び出し方法では、不明な数のパラメーターの配列を渡すことができます。(あなたのコードでは、あなたはあなたのデータがサービスから来ていると言っています、そしてそれが$ .whenを呼び出す唯一の方法です


15

ここでは、コードが完全に文書化されています。

// 1. Declare an array of 4 elements
var data = [1,2,3,4]; // the ids coming back from serviceA
// 2. Declare an array of Deferred objects
var processItemsDeferred = [];

// 3. For each element of data, create a Deferred push push it to the array
for(var i = 0; i < data.length; i++){
  processItemsDeferred.push(processItem(data[i]));
}

// 4. WHEN ALL Deferred objects in the array are resolved THEN call the function
//    Note : same as $.when(processItemsDeferred[0], processItemsDeferred[1], ...).then(everythingDone);
$.when.apply($, processItemsDeferred).then(everythingDone); 

// 3.1. Function called by the loop to create a Deferred object (data is numeric)
function processItem(data) {
  // 3.1.1. Create the Deferred object and output some debug
  var dfd = $.Deferred();
  console.log('called processItem');

  // 3.1.2. After some timeout, resolve the current Deferred
  //in the real world, this would probably make an AJAX call.
  setTimeout(function() { dfd.resolve() }, 2000);    

  // 3.1.3. Return that Deferred (to be inserted into the array)
  return dfd.promise();
}

// 4.1. Function called when all deferred are resolved
function everythingDone(){
  // 4.1.1. Do some debug trace
  console.log('processed all items');
}

7
$.when.apply($, array)と同じではありません$.when(array)。:それは同じだ$.when(array[0], array[1], ...)
ロケット危険物

1
これが.applyで使用される主な理由ですが、processItemsDeferredが持つ多くの要素がわからない
Pablo

2

残念ながら私はあなたたちに同意することはできません。

$.when.apply($, processItemsDeferred).always(everythingDone);

保留中の他の遅延オブジェクトがある場合でも、everythingDone1つの遅延オブジェクトが拒否されるとすぐに呼び出します。

完全なスクリプトを以下に示します(http://jsfiddle.net/をお勧めします)。

var data = [1,2,3,4]; // the ids coming back from serviceA
var processItemsDeferred = [];

for(var i = 0; i < data.length; i++){
  processItemsDeferred.push(processItem(data[i]));
}

processItemsDeferred.push($.Deferred().reject());
//processItemsDeferred.push($.Deferred().resolve());

$.when.apply($, processItemsDeferred).always(everythingDone); 

function processItem(data) {
  var dfd = $.Deferred();
  console.log('called processItem');

  //in the real world, this would probably make an AJAX call.
  setTimeout(function() { dfd.resolve(); }, 2000);    

  return dfd.promise();
}

function everythingDone(){
  alert('processed all items');
}

これはバグですか?私はこれを上記の紳士のように使用したいと思います。


1
最初の拒否は、常に発生しますが、.thenは発生しません。あなたの例から作った私のjsfiddle.net/logankd/s5dacgb3を見てください。この例では、JQuery 2.1.0を使用しています。
2015年

1
これは意図したとおりです。すべてが完了するのを待たずに、障害が発生したかどうかを確認するのではなく、何かが失敗したらすぐに知りたいケースがたくさんあります。特にエラーが発生しても処理を続行できない場合は、残りが完了する/失敗するまで待つのはなぜですか?他のコメントが示唆しているように、.thenまたは.fail&.doneペアを使用できます。
MPavlak

@GoneCoding役に立たない。OPは、apply()が何をするかを尋ね、あなたは絶対に使用してはならない恐ろしい代替案を提案しました:)これが反対投票ボタンの目的です。私はまた、なぜそれを行ったのかを理由として提供することを拒否するまでそれを使用しませんでした(何らかの理由で配列を回避するというあなたの好みよりも)
MPavlak

@GoneCodingその回答を削除していただきありがとうございます
MPavlak

1
@GoneCoding lol、私はあなたの解決策を読み、フィードバックを提供しました。元の質問への回答がありませんでした。なぜそれがあったのかを詳しく説明することはできませんでした。学習している人々にひどい解決策を提供するのはあなたのような人々です。あなたは明らかにJavaScriptのスキルが限られているため、私をn00bに連れて行っています。なぜそれが間違っていたのかを示し、あなたはコードを読むことさえできず、代わりに私が間違っていると私に言った。お疲れ様でした!
MPavlak

1

多分誰かがこれが便利だと思うかもしれません:

$.when.apply($, processItemsDeferred).then(everythingDone).fail(noGood);

拒否された場合、everythingDoneは呼び出されません。


0

$ .whenだけでは、渡されたすべてのpromiseが解決または拒否されたときにコールバックを呼び出すことができます。通常、$。whenは可変数の引数を取ります。.applyを使用すると、引数の配列を渡すことができます。これは非常に強力です。.applyの詳細:https ://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply


0

エレガントなソリューションをありがとう:

var promise;

for(var i = 0; i < data.length; i++){
  promise = $.when(promise, processItem(data[i]));
}

promise.then(everythingDone);

ただ1つのポイント:を使用resolveWithして一部のパラメーターを取得すると、最初のpromiseがundefinedに設定されているため、使用できなくなります。それを機能させるために私がしたこと:

// Start with an empty resolved promise - undefined does the same thing!
var promise;

for(var i = 0; i < data.length; i++){
  if(i==0) promise = processItem(data[i]);
  else promise = $.when(promise, processItem(data[i]));
}

promise.then(everythingDone);

2
それは機能しますが、実際にはエレガントではありません。最後の反復に "when(workToDo [0..i-1]、workToDo [i])"またはより明確に "以前のすべての作業とこの仕事が完了しました。」これは、約束をラッパーするときにi + 1があることを意味します。また、この種のものを実行する場合は、最初の反復をラップ解除するだけです。var promise = processItem(data [0]); for(var i = 1; i <data.length; i ++){promise = $ .when(promise、processItem(data [i])); }
MPavlak
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.