Cosa fa $ .when.apply ($, someArray)?


110

Sto leggendo di rinvii e promesse e continuo a incontrarmi $.when.apply($, someArray). Non sono un po 'chiaro su cosa fa esattamente, cercando una spiegazione che una riga funzioni esattamente (non l'intero frammento di codice). Ecco un po 'di contesto:

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()può essere utilizzato al posto di .thenin questo caso, solo FYI
Kevin B

2
fwiw, c'è una porta differita da sottolineare che consente di passare un singolo array in _.whenmodo da non doverlo utilizzareapply
Eevee



1
L'articolo a cui si riferisce l'OP nella sua prima frase ha spostato le posizioni - ora è all'indirizzo: flaviocopes.com/blog/deferreds-and-promises-in-javascript .
glaucon

Risposte:


161

.applyviene utilizzato per chiamare una funzione con un array di argomenti. Prende ogni elemento dell'array e lo utilizza come parametro per la funzione. .applypuò anche cambiare il context ( this) all'interno di una funzione.

Quindi, prendiamo $.when. Si usa dire "quando tutte queste promesse sono state risolte ... fai qualcosa". Richiede un numero infinito (variabile) di parametri.

Nel tuo caso, hai una serie di promesse; non sai a quanti parametri stai passando $.when. Passare l'array stesso a $.whennon funzionerebbe, perché si aspetta che i suoi parametri siano promesse, non un array.

È qui che .applyentra in gioco. Prende l'array e chiama $.whenogni elemento come parametro (e si assicura che thissia impostato su jQuery/ $), quindi funziona tutto :-)


3
quando più promesse vengono passate a $ .when metodo. In quale ordine verranno eseguiti? uno dopo l'altro o in parallelo?
Darshan

21
@Darshan: non "gestisci" le promesse. Aspetti che vengano risolti. Vengono eseguiti quando vengono creati, $.whenattende solo che siano finiti tutti prima di continuare.
Rocket Hazmat

1
che dire della differenza tra $.when($, arrayOfPromises).done(...) e $.when(null, arrayOfPromises).done(...) (che ho trovato entrambe come soluzioni proposte nei forum ...)
zeroquaranta

63

$ .when accetta un numero qualsiasi di parametri e si risolve quando tutti questi sono stati risolti.

anyFunction .apply (thisValue, arrayParameters) chiama la funzione anyFunction impostandone il contesto (thisValue sarà il this all'interno di quella chiamata di funzione) e passa tutti gli oggetti in arrayParameters come parametri individuali.

Per esempio:

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

Equivale a:

$.when(def1, def2)

Ma la applicano modo di chiamata consente di passare una matrice di numero imprecisato di parametri. (Nel codice, stai dicendo che i tuoi dati provengono da un servizio, quindi questo è l'unico modo per chiamare $ .quando )


15

Qui, il codice è completamente documentato.

// 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)non è lo stesso di $.when(array). È lo stesso di:$.when(array[0], array[1], ...)
Rocket Hazmat

1
Questo è il motivo principale per cui viene utilizzato con .Applicare , non sai hoy molti elementi processItemsDeferred ha
Pablo

2

Purtroppo non posso essere d'accordo con voi ragazzi.

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

Chiamerà everythingDonenon appena un differito viene rifiutato , anche se ci sono altri differiti in sospeso .

Ecco lo script completo (consiglio 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');
}

È un bug? Vorrei usarlo come il signore sopra descritto.


1
Il primo rifiuto attiverà il sempre, ma non il .then. Vedi il mio jsfiddle.net/logankd/s5dacgb3 che ho creato dal tuo esempio. Sto usando JQuery 2.1.0 in questo esempio.
Allineato il

1
Questo è come previsto. Ci sono un sacco di casi in cui vorresti sapere non appena qualcosa non funziona, non aspettare che tutto sia completato e controllare se ci sono stati errori. Soprattutto se l'elaborazione non può continuare dopo un errore, perché aspettare che il resto finisca / fallisca? Come suggerisce l'altro commento, puoi usare .then o .fail & .done pair.
MPavlak

@GoneCoding Non è utile. L'OP ha chiesto cosa fa apply () e tu hai suggerito una terribile alternativa che non dovrebbe mai essere usata :) che è ciò a cui serve il pulsante di voto basso. Inoltre non l'ho usato fino a quando non ti sei rifiutato di fornire PERCHÉ l'hai fatto così perché (più della tua preferenza per evitare gli array per qualche motivo)
MPavlak

@GoneCoding Grazie per aver preso
nota della

1
@GoneCoding lol, ho letto la tua soluzione e fornito feedback. non hai fornito una risposta alla domanda originale. Non riuscivi a spiegare perché era così com'era. Sono le persone come te che forniscono soluzioni terribili alle persone che stanno imparando. Hai chiaramente abilità javascript limitate e mi stai prendendo come un n00b. Ho indicato perché era sbagliato e non potevi nemmeno leggere il codice e invece dimmi che ho sbagliato. buon lavoro amico!
MPavlak

1

Forse qualcuno può trovarlo utile:

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

EverythingDone non viene chiamato in caso di rifiuto



0

Grazie per la tua elegante soluzione:

var promise;

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

promise.then(everythingDone);

Solo un punto: quando si utilizza resolveWithper ottenere alcuni parametri, si interrompe a causa della promessa iniziale impostata su undefined. Cosa ho fatto per farlo funzionare:

// 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
Anche se funziona, non è molto elegante. stai creando promesse che rappresentano il rinvio fino a-esimo in modo che l'ultima iterazione contenga "quando (workToDo [0..i-1], workToDo [i])" o più chiaramente "quando tutto il lavoro precedente e questo il lavoro è fatto". Ciò significa che hai i + 1 quando invii le tue promesse. Inoltre, quando fai questo genere di cose, scartare semplicemente la prima iterazione. var promise = processItem (data [0]); for (var i = 1; i <data.length; i ++) {promise = $ .when (promise, processItem (data [i])); }
MPavlak
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.