115

我正在阅读有关 Deferreds 和 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');
}
4

7 回答 7

170

.apply用于调用带有参数数组的函数。它获取数组中的每个元素,并将每个元素用作函数的参数。 还可以更改函数内部.apply的上下文 ( )。this

所以,让我们采取$.when. 它用来说“当所有这些承诺都得到解决时......做某事”。它需要无限(可变)数量的参数。

就您而言,您有一系列承诺;你不知道你传递了多少参数$.when。将数组本身传递给$.when将不起作用,因为它希望其参数是承诺,而不是数组。

这就是.apply进来的地方。它接受数组,并$.when以每个元素作为参数调用(并确保将this其设置为jQuery/ $),所以一切正常:-)

于 2013-02-08T16:34:26.680 回答
64

$.when接受任意数量的参数,并所有这些都解决后解析。

anyFunction .apply(thisValue, arrayParameters) 调用函数anyFunction设置其上下文(thisValue 将是该函数调用中的this)并将 arrayParameters 中的所有对象作为单独的参数传递。

例如:

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

是相同的:

$.when(def1, def2)

但是调用的apply方式允许您传递一个未知数量的参数数组。(在您的代码中,您是说您的数据来自服务,那么这是调用$.when的唯一方法)

于 2013-02-08T16:40:02.687 回答
17

在这里,代码完全记录在案。

// 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');
}
于 2013-02-08T16:36:56.880 回答
2

不幸的是,我不能同意你们。

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

everythingDone只要有一个 deferred 被拒绝就会调用,即使还有其他的 deferred等待处理

这是完整的脚本(我推荐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');
}

这是一个错误吗?我想像上面描述的那样使用它。

于 2014-08-12T08:58:37.943 回答
2

也许有人会发现这很有用:

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

任何拒绝都不会调用everythingDone

于 2019-01-14T13:09:27.077 回答
0

单独使用 $.when 可以在传递给它的每个 Promise 都被解析/拒绝时调用回调。通常, $.when 接受可变数量的参数,使用 .apply 可以将参数数组传递给它,它非常强大。有关 .apply 的更多信息:https ://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply

于 2013-02-08T16:34:35.500 回答
0

感谢您的优雅解决方案:

var promise;

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

promise.then(everythingDone);

只有一点:当resolveWith用于获取一些参数时,由于初始承诺设置为未定义,它会中断。我做了什么使它工作:

// 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);
于 2016-04-21T15:58:27.007 回答