2013-02-08 106 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