$.when.apply($, someArray) 做啥?

Posted

技术标签:

【中文标题】$.when.apply($, someArray) 做啥?【英文标题】:What does $.when.apply($, someArray) do?$.when.apply($, someArray) 做什么? 【发布时间】:2013-01-24 11:34:26 【问题描述】:

我是reading about Deferreds and Promises,并且经常遇到$.when.apply($, someArray)。我有点不清楚这到底是做什么的,正在寻找 一行 完全有效的解释(不是整个代码 sn-p)。这是一些上下文:

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');

【问题讨论】:

.done() 在这种情况下可以用来代替.then,仅供参考 fwiw,有一个下划线的延迟端口允许将单个数组传递给_.when,因此您不需要使用apply 了解更多关于.apply的信息:developer.mozilla.org/en-US/docs/javascript/Reference/…。 相关:***.com/questions/1986896/… OP 在他的第一句话中提到的文章已经移动了位置 - 现在在:flaviocopes.com/blog/deferreds-and-promises-in-javascript。 【参考方案1】:

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

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

在您的情况下,您有一系列承诺;您不知道要传递给$.when 的参数数量。将数组本身传递给 $.when 是行不通的,因为它希望它的参数是 promise,而不是数组。

这就是 .apply 出现的地方。它接受数组,并以每个元素作为参数调用 $.when(并确保将 this 设置为 jQuery/$),然后它所有作品:-)

【讨论】:

当多个 Promise 被传递给 $.when 方法时。他们将以什么顺序执行?一个接一个还是并行? @Darshan:你不会“运行”承诺。你等待他们解决。它们在创建时执行,$.when 只是等待它们全部完成后再继续。 $.when($, arrayOfPromises).done(...)$.when(null, arrayOfPromises).done(...) 之间的区别是什么(我在论坛中发现两者都是建议的解决方案...)【参考方案2】:

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

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

例如:

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

等同于:

$.when(def1, def2)

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

【讨论】:

【参考方案3】:

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

// 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');

【讨论】:

$.when.apply($, array)$.when(array)相同。等同于:$.when(array[0], array[1], ...) 这就是为什么与 .apply 一起使用的主要原因,你不知道 processItemsDeferred 有很多元素【参考方案4】:

很遗憾,我不能同意你们的看法。

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

一旦有一个延期被拒绝,将立即致电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');

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

【讨论】:

第一个拒绝会触发always,但不会触发.then。请参阅我根据您的示例制作的jsfiddle.net/logankd/s5dacgb3。我在这个例子中使用的是 JQuery 2.1.0。 这符合预期。在很多情况下,您都想在 something 失败时立即了解,而不是等待一切都完成并检查是否有失败。特别是如果处理在任何失败后无法继续,为什么要等待其余的完成/失败?正如其他评论所建议的那样,您可以使用 .then 或 .fail & .done 对。 @GoneCoding 没用。 OP 询问 apply() 的作用,您提出了一个永远不应该使用的可怕替代方案:) 这就是否决投票按钮的用途。我也没有使用它,直到您拒绝提供您为什么这样做(不仅仅是出于某种原因您更喜欢避免使用数组) @GoneCoding 感谢您记下这个答案 @GoneCoding 大声笑,我阅读了您的解决方案并提供了反馈。您没有提供原始问题的答案。你无法详细说明为什么会这样。像你这样的人为正在学习的人提供了糟糕的解决方案。你显然只有有限的 javascript 技能,并且正在让我成为 n00b。我指出了错误的原因,您甚至无法阅读代码,而是告诉我我错了。干得好伙计!【参考方案5】:

也许有人会觉得这很有用:

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

在任何拒绝的情况下都不会调用everythingDone

【讨论】:

【参考方案6】:

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

【讨论】:

【参考方案7】:

感谢您优雅的解决方案:

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);

【讨论】:

虽然这确实有效,但它并不是很优雅。您正在创建代表最多延迟完成的承诺,以便最后一次迭代包含“when(workToDo[0..i-1], workToDo[i])”或更明确地说“当所有以前的工作和这个工作已经完成”。这意味着当包装你的承诺时你有 i + 1 。此外,在做这种事情时,只需打开第一次迭代。 var promise = processItem(数据[0]); for(var i = 1; i

以上是关于$.when.apply($, someArray) 做啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 JDBC 中使用 unnest(array[someArray]) 发送三个数组的异常

JS对象解构

使用JavaScript从数组中删除对象

Jquery each - 停止循环并返回对象

拆分字符串不工作

return 会停止循环吗?