Що робить $ .when.apply ($, someArray)?


110

Я читаю про "Відстрочки" та "Обіцянки" та продовжую стикатися $.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
Kevin B

2
fwiw, є підкреслений порт для підкреслення, який дозволяє передавати один масив, щоб _.whenвам не потрібно було користуватисяapply
Eevee

Дізнайтеся більше про .apply: developer.mozilla.org/en-US/docs/JavaScript/Reference / ... .
Фелікс Клінг


1
Стаття, на яку посилається ОП у своєму першому реченні, перемістила місця - зараз це знаходиться за адресою: flaviocopes.com/blog/deferreds-and-promises-in-javascript .
glaucon

Відповіді:


161

.applyвикористовується для виклику функції з масивом аргументів. Він приймає кожен елемент у масиві та використовує кожен як параметр функції. .applyтакож може змінити контекст ( this) всередині функції.

Отже, візьмемо $.when. Він звик говорити "коли всі ці обіцянки будуть вирішені ... зроби щось". Він займає нескінченну (змінну) кількість параметрів.

У вашому випадку у вас є масив обіцянок; ви не знаєте, до яких параметрів ви переходите $.when. Передача самого масиву не $.whenбуде працювати, оскільки він очікує, що його параметри будуть обіцянками, а не масивом.

Ось де це .applyвходить. Він бере масив і викликає $.whenкожен параметр як параметр (і гарантує, що thisзначення встановлено в jQuery/ $), тож все працює :-)


3
коли декілька обіцянок передаються методу $ .when. У якому порядку вони будуть виконуватись? одна за одною чи паралельно?
Даршан

21
@Darshan: Ви не виконуєте обіцянок. Ви чекаєте їх вирішення. Вони виконуються під час створення, $.whenпросто чекають, коли всі вони будуть закінчені, перш ніж продовжувати.
Ракета Хазмат

1
як щодо різниці між $.when($, arrayOfPromises).done(...) і $.when(null, arrayOfPromises).done(...) (що я знайшов як запропоновані рішення на форумах ...)
zeroquaranta

63

$ .when приймає будь-яку кількість параметрів і вирішує, коли всі вони вирішені.

anyFunction .apply (thisValue, arrayParameters) викликає функцію anyFunction, встановлюючи її контекст (thisValue буде цим в рамках виклику функції) і передає всі об'єкти в arrayParameters як окремі параметри.

Наприклад:

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

Це те саме, що:

$.when(def1, def2)

Але застосувати спосіб виклику дозволяє передати масив невідомої кількості параметрів. (У своєму коді ви говорите, що ваші дані надходять від послуги, то це єдиний спосіб зателефонувати в $ .when )


15

Тут код повністю задокументований.

// 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], ...)
Ракета Hazmat

1
Ось головна причина, чому використовується з .apply , ви не знаєте, як багато елементів процесItemsDeferred
Пабло

2

На жаль, я не можу погодитися з вами, хлопці.

$.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');
}

Це ця помилка? Я хотів би використати це так, як описав його пан.


1
Перша відхилення буде завжди стріляти, але не тоді. Дивіться мій jsfiddle.net/logankd/s5dacgb3, який я зробив з вашого прикладу. У цьому прикладі я використовую JQuery 2.1.0.
Вирівняно

1
Це за призначенням. Є маса випадків, коли ви хочете дізнатися, як тільки щось виходить з ладу, не чекайте, коли все завершиться, і перевіряйте, чи були збої. Особливо, якщо обробка не може продовжуватися після будь-якого збою, навіщо чекати, коли решта закінчиться / вийде з ладу? Як запропоновано іншим коментарем, ви можете використовувати .then або .fail & .done.
MPavlak

@GoneCoding Це не корисно. ОП запитала, що робиться у застосуванні (), і ви запропонували жахливу альтернативу, яку ніколи не слід використовувати :) що це те, для чого потрібна кнопка "вниз". Я також не користувався ним, поки ви не відмовились надати ЧОМУ ви це зробили, чому (більше, ніж ви хочете чомусь уникати масивів)
MPavlak,

@GoneCoding Дякую, що ви зняли цю відповідь
MPavlak

1
@GoneCoding lol, я прочитав ваше рішення та надав відгуки. ви не надали відповіді на початкове запитання. Ви не змогли уточнити, чому так було. Такі люди, як ви, надають жахливі рішення людям, які навчаються. Ви явно володієте обмеженими навичками JavaScript і вважаєте мене n00b. Я вказав, чому це було неправильно, і ви навіть не могли прочитати код і натомість сказали мені, що я помиляюся. хороший приятель роботи!
МПавлак

1

Можливо, комусь це стане корисним:

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

everythingDone не викликається у разі будь-якого відхилення


0

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


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

2
Хоча це і працює, це не дуже елегантно. ви створюєте обіцянки, які представляють дострокове відкладене виконання, щоб остання ітерація містила "коли (workToDo [0..i-1], workToDo [i])" або більш просто ", коли вся попередня робота та це робота робиться ». Це означає, що у вас є + 1, коли обгортки над своїми обіцянками. Крім того, виконуючи подібні речі, просто розгорніть першу ітерацію. var promis = processItem (дані [0]); for (var i = 1; i <data.length; i ++) {obe = $ .when (обіцянка, processItem (дані [i])); }
MPavlak
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.