2013-03-19 5 views
20

Sto cercando di creare quella che a mio avviso si chiama "cascata". Voglio elaborare sequenzialmente una serie di funzioni asincrone (jQuery promette).Ciclo asincrono di jQuery differite (promesse)

Ecco un esempio forzato:

function doTask(taskNum){ 
    var dfd = $.Deferred(), 
     time = Math.floor(Math.random()*3000); 

    setTimeout(function(){ 
     console.log(taskNum); 
     dfd.resolve(); 
    },time) 

    return dfd.promise(); 
} 

var tasks = [1,2,3]; 

for (var i = 0; i < tasks.length; i++){ 
    doTask(tasks[i]); 
} 

console.log("all done"); 

Vorrei che per completare l'operazione nell'ordine in cui vengono eseguite (presenti nella matrice). Quindi, in questo esempio, voglio che esegua l'attività 1 e attenda che si risolva, quindi esegui l'attività 2 in attesa che si risolva, esegui l'attività 3 ecc. E il registro "Tutto fatto".

Forse questo è davvero ovvio ma ho cercato di capirlo tutto il pomeriggio.

risposta

19

mi piacerebbe provare con $().queue invece di $.Deferred qui. Aggiungi le funzioni a una coda e chiama solo la successiva quando è pronta.

function doTask(taskNum, next){ 
    var time = Math.floor(Math.random()*3000); 

    setTimeout(function(){ 
     console.log(taskNum); 
     next(); 
    },time) 
} 

function createTask(taskNum){ 
    return function(next){ 
     doTask(taskNum, next); 
    } 
} 

var tasks = [1,2,3]; 

for (var i = 0; i < tasks.length; i++){ 
    $(document).queue('tasks', createTask(tasks[i])); 
} 

$(document).queue('tasks', function(){ 
    console.log("all done"); 
}); 

$(document).dequeue('tasks'); 
+1

questo è unico nel suo genere! risolto un problema molto difficile per me. Grazie mille. –

0

Interessante sfida. Quello che ho scoperto è una funzione ricorsiva che accetta un elenco e un indice di inizio opzionale.

Here is a link to the jsFiddle che ho testato con un elenco di lunghezze e intervalli diversi.

Suppongo che tu abbia un elenco di funzioni che restituiscono promesse (non un elenco di numeri). Se si dispone di un elenco di numeri cambieresti questa parte

$.when(tasks[index]()).then(function(){ 
    deferredSequentialDo(tasks, index + 1); 
}); 

a questo

/* Proxy is a method that accepts the value from the list 
    and returns a function that utilizes said value 
    and returns a promise */ 
var deferredFunction = myFunctionProxy(tasks[index]); 

$.when(tasks[index]()).then(function(){ 
    deferredSequentialDo(tasks, index + 1); 
}); 

Non sono sicuro di quanto è grande la vostra lista di funzioni potrebbe essere, ma basta essere consapevoli che il browser trattiene le risorse dalla prima chiamata differitaSequentialDo fino a quando non sono tutte terminate.

+0

* deferredSync * suona come un ossimoro – Bergi

+0

Essa, tuttavia ho potuto vedere un uso per questo se si dispone di un paio di chiamate AJAX che si desidera eseguire in modo sincrono (cosa che devo in effetti mi sono imbattuto nella mia esperienza) – awbergs

+0

Ajax * è * non sincrono. Cosa intendi? – Bergi

9

Per una cascata, è necessario un ciclo asincrono:

(function step(i, callback) { 
    if (i < tasks.length) 
     doTask(tasks[i]).then(function(res) { 
      // since sequential, you'd usually use "res" here somehow 
      step(i+1, callback); 
     }); 
    else 
     callback(); 
})(0, function(){ 
    console.log("all done"); 
}); 
4

Date un'occhiata ai $.when e then metodi per l'esecuzione deferreds.

Le cascate vengono utilizzate per convogliare i valori di ritorno da uno al successivo, in serie. Sembrerebbe qualcosa like this.

function doTask (taskNum) { 
    var dfd = $.Deferred(), 
     time = Math.floor(Math.random() * 3000); 

    console.log("running task " + taskNum); 

    setTimeout(function(){ 
     console.log(taskNum + " completed"); 
     dfd.resolve(taskNum + 1); 
    }, time) 

    return dfd.promise(); 
} 

var tasks = [1, 2, 3]; 

tasks 
    .slice(1) 
    .reduce(function(chain) { return chain.then(doTask); }, doTask(tasks[0])) 
    .then(function() { console.log("all done"); }); 

Nota l'argomento passato alla resolve. Questo viene passato alla funzione successiva nella catena. Se si desidera solo per farli funzionare in serie senza tubazioni in argomenti, si può prendere che fuori e modificare la chiamata ridurre al .reduce(function(chain, taskNum) { return chain.then(doTask.bind(null, taskNum)); }, doTask(tasks[0]));

E in parallelo si dovrebbe guardare like this:

var tasks = [1,2,3].map(function(task) { return doTask(task); }); 

$.when.apply(null, tasks).then(function() { 
    console.log(arguments); // Will equal the values passed to resolve, in order of execution. 
}); 
+0

Grazie gumballhead, questo ha fatto il trucco per me. – CrystalVisions

+0

questa risposta in realtà non utilizza i valori nelle attività. le attività potrebbero essere [1,5,14] e produrre ancora la stessa produzione – thedarklord47

5

È possibile creare un $ risolto.Differite e basta aggiungere alla catena con ogni iterazione:

var dfd = $.Deferred().resolve(); 
tasks.forEach(function(task){ 
    dfd = dfd.then(function(){ 
     return doTask(task); 
    }); 
}); 

Passo dopo passo seguente che sta accadendo:

//begin the chain by resolving a new $.Deferred 
var dfd = $.Deferred().resolve(); 

// use a forEach to create a closure freezing task 
tasks.forEach(function(task){ 

    // add to the $.Deferred chain with $.then() and re-assign 
    dfd = dfd.then(function(){ 

     // perform async operation and return its promise 
     return doTask(task); 
    }); 

}); 

Personalmente, trovo questo pulitore di ricorsione e più familiare di $() coda. (jQuery API for $(). queue è confusa poiché è progettata per le animazioni, è anche probabile che tu stia usando $ .Deferred in altri punti del tuo codice). Ha anche i vantaggi del trasferimento standard dei risultati in cascata attraverso resolve() nell'operazione asincrona e consentendo l'allegato di una proprietà $ .done.

Qui è in un jsFiddle

+0

Davvero come questo, grazie! – VeldMuijz