Что делает $.when.apply($, someArray)?

Я читаю о Отложенных и 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

.apply используется для вызова функции с массивом аргументов. Он принимает каждый элемент в массиве и использует каждый в качестве параметра для функции. .apply также может изменять контекст (this) внутри функции.

Итак, возьмите $.when. Раньше говорилось "когда все эти promises разрешены... что-то делать". Он принимает бесконечное (переменное) количество параметров.

В вашем случае у вас есть массив promises; вы не знаете, сколько параметров вы переходите в $.when. Передача самого массива в $.when не будет работать, поскольку он ожидает, что его параметры будут promises, а не массивом.

То, что входит .apply. Он принимает массив и вызывает $.when с каждым элементом в качестве параметра (и гарантирует, что значение this установлено на jQuery/$), так что тогда все работы: -)

Ответ 2

$. когда принимает любое количество параметров и решает, когда все они разрешены.

anyFunction.apply(thisValue, arrayParameters) вызывает функцию anyFunction, устанавливающую ее контекст (thisValue будет это) и все параметры в параметрах arrayParameters.

Например:

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

То же, что и:

$.when(def1, def2)

Но применяемый способ вызова позволяет передать массив неизвестного числа параметров. (В вашем коде вы говорите, что данные поступают из службы, то это единственный способ вызвать $.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');
}

Ответ 4

К сожалению, я не могу согласиться с вами, ребята.

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

Вызовите everythingDone, как только один отложенный получит отклонен, даже если есть другие отложенные ожидающие.

Вот полный script (рекомендую 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');
}

Это ошибка? Я хотел бы использовать это, как описанный выше джентльмен.

Ответ 5

$, когда только позволяет вызвать обратный вызов, когда каждый переданный ему promises разрешен/отклонен. Обычно, $.when принимает переменное количество аргументов, использование .apply позволяет передать ему массив аргументов, это очень мощный. Для получения дополнительной информации о .apply: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply

Ответ 6

Спасибо за ваше элегантное решение:

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