$.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) 做啥?的主要内容,如果未能解决你的问题,请参考以下文章