2013-02-08 108 views
92

我是reading about Deferreds and Promises,並且不斷遇到$.when.apply($, someArray)。我有點不清楚這到底是什麼,尋找一個解釋,一行工作正好(不是整個代碼片段)。下面是一些背景:

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 – 2013-02-08 16:39:53

+2

FWIW,有一個遞延端口強調,允許通過一個單一的array to'_.when',所以你不需要使用'apply' – Eevee 2013-02-08 16:45:12

+0

瞭解更多關於'.apply'的信息:https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function /應用。 – 2013-02-08 16:48:38

回答

138

.apply用於調用函數與參數數組。它接受數組中的每個元素,並將每個元素用作函數的參數。 .apply也可以更改函數內的上下文(this)。

那麼,我們來看看$.when。它習慣於說「當所有這些承諾都解決了......做些什麼」。它需要一個無限(可變)數量的參數。

就你而言,你有一系列的承諾;你不知道你傳遞給$.when的參數有多少。將數組本身傳遞給$.when將無法​​工作,因爲它期望其參數是promise,而不是數組。

這就是.apply用武之地。它採用陣列,並與每一個元素作爲參數調用$.when(並確保該this設置爲jQuery/$),所以後來這一切工作:-)

+3

將多個promise傳遞給$ .when方法時。他們將以什麼順序執行?一個接一個還是並行? – Darshan 2013-11-25 17:23:04

+20

@Darshan:你不會「運行」承諾。你等待他們解決。它們在創建時執行,'$ .when'只等待它們完成後才能繼續。 – 2013-12-02 15:27:27

+0

什麼'$。當($,arrayOfPromises).done(...)' 和 '$。當(NULL,arrayOfPromises).done(...)' 之間的差值(我發現它們在論壇中都是建議的解決方案......) – tHomas 2017-06-21 08:34:32

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],...)相同' – 2013-02-08 16:38:27

+1

這就是爲什麼使用_.apply_的主要原因,你不知道許多元素processItemsDeferred有 – 2013-02-08 16:42:16

56

$.when接受任何數量的參數和解決所有的這些都解決了。

anyFunction。適用(thisValue,arrayParameters)調用函數anyFunction設置其上下文(thisValue將是該函數調用中),並在arrayParameters作爲單獨的參數傳遞的所有對象。

例如:

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

是一樣的:

$.when(def1, def2) 

應用調用的方法允許你傳遞未知數量的參數數組。 (在你的代碼,你說你數據來自一個服務,然後就是打電話$。當的唯一途徑)

+1

Best舉個例子,謝謝! – Dan 2017-04-28 16:16:07

1

可惜我不能跟你們同意。

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

會盡快致電everythingDone作爲一個延緩得到拒絕,即使有其他deferreds是掛起

繼承人的完整的腳本(我建議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

第一個拒絕將會激發總是,但不是。然後。請參閱我在您的示例中製作的http://jsfiddle.net/logankd/s5dacgb3/。在這個例子中我使用JQuery 2.1.0。 – Aligned 2015-01-30 19:30:20

+1

這是預期的。有很多情況下,只要有*失敗,就不會等待一切完成並檢查是否有失敗。特別是如果在任何失敗後處理不能繼續,爲什麼要等待其餘的完成/失敗?正如其他評論建議,您可以使用.then或.fail&.done對。 – MPavlak 2016-04-27 17:31:43

+0

@GoneCoding它沒用。 OP詢問apply()是做什麼的,並且你提出了一個永遠不應該使用的可怕替代方案:)這就是down投票按鈕的用途。我也沒有使用它,直到你拒絕提供爲什麼你這麼做(爲了某種原因,你更喜歡避免使用數組) – MPavlak 2016-04-27 19:28:22

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]);對於(var i = 1; i MPavlak 2016-04-27 17:27:33