2013-02-08 7 views
92

私はreading about Deferreds and 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 –

+2

FWIW、それを強調するために繰延ポートがあるのは、シングルを渡すことができます配列を '_.when'にして、' apply'を使う必要はありません – Eevee

+0

'.apply'の詳細:https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function /適用する。 –

答えて

138

.apply:ここではいくつかの状況です。配列内の各要素をとり、それぞれを関数のパラメータとして使用します。 .applyは、関数内のコンテキスト(this)を変更することもできます。

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

あなたの場合、約束事があります。あなたはどれくらい多くのパラメータを$.whenに渡すのか分かりません。配列自体を$.whenに渡すことは、配列ではなくパラメータを約束していると予想されるため、機能しません。

.applyの出番だ。それは配列を受け取り、そしてパラメータとして各要素に$.whenを呼び出し(および確認thisjQuery/$に設定されます)ので、それはすべて:-)

+3

複数の約束が$ .whenメソッドに渡される場合。彼らはどのような順序で実行されますか? 1つは他の後にまたは並列に? – Darshan

+20

@ダルシャン:あなたは約束を「実行」しません。あなたはそれらが解決されるのを待つ。それらは作成されたときに実行され、 '$ .when'はそれらのすべてが終了するまで待ってから続行します。 –

+0

何 '$ .when($、arrayOfPromises)の.done(...)' と '$ .when(ヌル、arrayOfPromises)の.done(...)' (の違いについて私は両方をフォーラムで提案された解決策として見つけました...) – tHomas

12

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

// 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が持っています。 –

56

$.when任意の数のパラメータをとり、これらのすべてが解決したときを解決します。

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

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

は同じです:

$.when(def1, def2) 

しかしは、呼び出し元の方法を適用するあなたはパラメータの数が不明の配列を渡すことができます。

+1

ベスト遠くの例、ありがとう! – Dan

1

(あなたのコードでは、あなたがあなたデータがサービスから来て、そしてそれは$ .whenを呼び出すための唯一の方法であると言っている)残念ながら、私はあなたたちに同意することはできません。

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

はすぐに延期1は保留ている他のDeferredの作り方があっても、を拒否しを取得するようeverythingDoneを呼び出します。相続人

フルスクリプト(私は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'); 
} 

またこのバグ?私は上記の紳士のようにこれを使用したいと思います。あなたのエレガントなソリューションのための

+0

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

+1

これは意図した通りです。 *何かが失敗するとすぐに知りたいと思うたくさんのケースがあります。すべてが完了するのを待って、失敗があるかどうかを確認する必要はありません。特に、処理が失敗した後で処理を続行できない場合は、残りの処理が終了するのを待つ理由は何ですか?他のコメントが示唆したように、.thenまたは.failと.doneのペアを使用することができます。 – MPavlak

+0

@GoneCoding便利ではありません。 OPは、apply()が何をしているのかを尋ねました。決して使用されるべきではない恐ろしい代替案を提案しました:)それは、下の投票ボタンのためのものです。私はあなたがなぜそれをしたのかを拒否するまで、それを使用しませんでした。なぜなら、(何らかの理由で配列を避けるようにあなたの好みよりも) – MPavlak

0

ありがとう:

var promise; 

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

promise.then(everythingDone); 

ジャストワンポイント:いくつかのパラメータを取得するためにresolveWithを使用している場合、それが原因で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); 
+1

それは動作しますが、それは本当にエレガントではありません。最後の反復に「when(workToDo [0..i-1]、workToDo [i])」または「もっと明白に」含まれるように、前回の作業が完了したときに、仕事は終わった "。これは、あなたの約束以上のラッパーを使用しているときにi + 1を持つことを意味します。 また、このようなことをするときは、最初の繰り返しのラップを解除するだけです。 var promise = processItem(data [0]); for(var i = 1; i MPavlak

関連する問題