web-dev-qa-db-fra.com

Que fait $ .when.apply ($, someArray)?

Je suis en train de lire sur les différés et les promesses et je continue à rencontrer $.when.apply($, someArray). Je ne sais pas trop ce que cela fait exactement, je cherche une explication sur le fait que une ligne fonctionne exactement (pas l'extrait de code complet). Voici un peu de contexte: 

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');
}
102
manafire

.apply est utilisé pour appeler une fonction avec un tableau d'arguments. Il prend chaque élément du tableau et les utilise comme paramètre de la fonction. .apply peut également changer le contexte (this) à l'intérieur d'une fonction.

Alors, prenons $.when. On dit "quand toutes ces promesses sont résolues ... fais quelque chose". Il faut un nombre infini (variable) de paramètres.

Dans votre cas, vous avez un éventail de promesses. vous ne savez pas combien de paramètres vous passez à $.when. Passer le tableau lui-même à $.when ne fonctionnerait pas, car il s'attend à ce que ses paramètres soient des promesses, pas un tableau.

C'est là que .apply entre en jeu. Il prend le tableau et appelle $.when avec chaque élément en tant que paramètre (et s'assure que la variable this est définie sur jQuery/$), donc tout fonctionne :-)

152
Rocket Hazmat

$ .when prend un nombre quelconque de paramètres et résout lorsque tous ceux-ci ont été résolus.

anyFunction .apply (thisValue, arrayParameters) appelle la fonction anyFunction en définissant son contexte (thisValue sera le this dans cet appel de fonction) et transmet tous les objets de arrayParameters en tant que paramètres individuels.

Par exemple:

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

Est le même que:

$.when(def1, def2)

Mais la méthode d'appel apply vous permet de transmettre un tableau de paramètres inconnus. (Dans votre code, vous dites que vous data venez d'un service, c'est le seul moyen d'appeler $ .when)

61

Ici, le code est entièrement documenté.

// 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');
}
13
Yanick Rochon

Peut-être que quelqu'un peut trouver cela utile:

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

everythingDone n'est pas appelé en cas de rejet

1
Vlado Kurelec

Malheureusement, je ne peux pas être d'accord avec vous les gars.

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

Appelle everythingDone dès qu'un différé obtient rejeté, même s'il existe d'autres différés qui sont en attente.

Voici le script complet (je recommande 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');
}

Est-ce un bug? Je voudrais utiliser ceci comme le monsieur ci-dessus l'a décrit.

1
user3388213

$ .when seul permet d’appeler un rappel lorsque toutes les promesses qui lui sont transmises sont résolues/rejetées. Normalement, $ .when prend un nombre variable d'arguments, utiliser .apply permet de lui passer un tableau d'arguments, c'est très puissant. Pour plus d'informations sur .apply: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply

0
Roger C

Merci pour votre solution élégante: 

var promise;

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

promise.then(everythingDone);

Juste un point: lorsqu’on utilise resolveWith pour obtenir certains paramètres, il se brise à cause de la promesse initiale définie sur non définie. Ce que j'ai fait pour que ça marche:

// 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);
0
user3544352