2013-03-19 42 views
20

Ich versuche zu schaffen, was ich denke, wird als "Wasserfall" bezeichnet. Ich möchte ein Array von asynchronen Funktionen (jQuery Versprechungen) sequentiell verarbeiten.Asynchrone Schleife von jQuery Verzögerte (Versprechen)

Hier ist ein konstruiertes Beispiel:

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

Ich mag es die Aufgabe, in der Reihenfolge vervollständigen sie (in der Array) ausgeführt werden. In diesem Beispiel möchte ich, dass es Aufgabe 1 ausführt und darauf wartet, dass es aufgelöst wird, dann Aufgabe 2 abwarten, bis es aufgelöst wird, Aufgabe 3 usw. und das Protokoll "alles erledigt".

Vielleicht ist das wirklich offensichtlich, aber ich habe versucht, dies den ganzen Nachmittag herauszufinden.

Antwort

19

Ich würde versuchen, mit $().queue statt $.Deferred hier. Fügen Sie die Funktionen zu einer Warteschlange hinzu, und rufen Sie die nächste nur dann auf, wenn sie bereit ist.

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

das ist einzigartig! löste ein sehr schweres Problem für mich. vielen Dank. –

0

Interessante Herausforderung in der Tat. Was ich herausgefunden habe, ist eine rekursive Funktion, die eine Liste und einen optionalen Startindex akzeptiert.

Here is a link to the jsFiddle habe ich mit ein paar verschiedenen Listenlängen und Intervallen getestet.

Ich nehme an, Sie haben eine Liste von Funktionen, die Versprechen (keine Liste von Zahlen) zurückgeben. Wenn Sie eine Liste von Zahlen zu tun haben würden Sie diesen Teil ändern

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

diese

/* 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); 
}); 

Ich bin nicht sicher, wie groß Ihre Liste von Funktionen sein könnte, aber nur bewusst sein, dass der Browser Halten Sie sich an den Ressourcen vom ersten Aufruf von deferredSequentialDo fest, bis alle fertig sind.

+0

* deferredSync * klingt wie ein Oxymoron – Bergi

+0

Es hat jedoch konnte ich für diese eine Anwendung sehen, wenn Sie ein paar Ajax haben Anrufe, die Sie synchron ausgeführt werden soll (was ich habe tatsächlich in meiner Erfahrung gestoßen) – awbergs

+0

Ajax * ist * nicht synchron. Was meinen Sie? – Bergi

9

Für einen Wasserfall, benötigen Sie einen Asynchron-Schleife:

(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

Werfen Sie einen Blick auf die $.when und then Methoden für deferreds läuft.

Wasserfälle werden verwendet, um die Rückgabewerte von einer verzögerten zur nächsten in Serie zu leiten. Es würde etwas aussehen 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"); }); 

Beachten Sie, dass das Argument an resolve übergeben wurde. Das wird an die nächste Funktion in der Kette übergeben. Wenn Sie nur in Argumente ohne Verrohrung sie in Reihe ausführen möchten, können Sie das herausnehmen und den Anruf reduzieren ändern .reduce(function(chain, taskNum) { return chain.then(doTask.bind(null, taskNum)); }, doTask(tasks[0]));

Und parallel würde es aussehen 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

Danke Gumballhead, das hat den Trick für mich gemacht. – CrystalVisions

+0

Diese Antwort verwendet nicht die Werte in Aufgaben. Aufgaben könnten [1,5,14] sein und immer noch die gleiche Ausgabe produzieren – thedarklord47

5

Sie können ein aufgelöstes $ erstellen.Latente und nur mit jeder Iteration der Kette hinzufügen:

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

Schritt für Schritt das folgende geschieht:

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

}); 

Persönlich finde ich diese sauberer als Rekursion und vertrauter als $() Warteschlange. (Die jQuery-API für $(). queue ist verwirrend, da sie für Animationen entworfen wurde. Wahrscheinlich verwenden Sie auch $ .Deferred an anderen Stellen in Ihrem Code). Es hat auch die Vorteile der Standard-Übertragung von Ergebnissen im Wasserfall durch resolve() in der asynchronen Operation und ermöglicht das Anhängen einer $ .done -Eigenschaft.

Hier ist es in einem jsFiddle

+0

Wirklich wie dieser, danke! – VeldMuijz