2016-07-17 3 views
1

Ich habe eine Funktion foo, die mehrere (parallele) asynchrone Aufrufe in einer Schleife macht. Ich muss irgendwie warten, bis die Ergebnisse aller Anrufe verfügbar sind. Wie kann ich die vollständigen Ergebnisse von foo zurückgeben oder eine andere Verarbeitung auslösen, nachdem der Daten verfügbar ist?Wie gebe ich die kumulierten Ergebnisse mehrerer (paralleler) asynchroner Funktionsaufrufe in einer Schleife zurück?

Ich habe versucht, jedes Ergebnis zu einem Array hinzuzufügen, aber dann wird das Array erst nach dem Punkt, an dem ich es verwenden muss, bestückt.

function foo() { 
    var results = []; 

    for (var i = 0; i < 10; i++) { 
     someAsyncFunction({someParam:i}, function callback(data) { 
     results.push(data); 
     }); 
    } 
    return results; 
} 

var result = foo(); // It always ends up being an empty array at this point. 

Hinweis: Diese Frage ist bewusst generisch im Sinne der bestehenden generischen "How do I return the response from an asynchronous call?" question. Diese Frage enthält einige ausgezeichnete Antworten, deckt jedoch nicht mehrere Async-Aufrufe ab. Es gibt einige andere Fragen, die mehrere Anrufe erwähnen, aber ich konnte keine Loop-basierte diejenigen finden, und einige hatten nur jQuery Antworten, etc. Ich bin der Hoffnung, hier einige allgemeine Techniken, die auf einer bestimmten Bibliothek abhängen.

+1

Ah, ich eine kanonische auf diese für soo lange :-) wollte – Bergi

+0

nicht sicher, ob wir Parallelbetrieb, sequentiellen Betrieb oder beides (I‘abdecken wollen eher separate Fragen). Dies sollte klar in der Frage (und möglicherweise sogar Titel) angegeben werden – Bergi

+0

Wenn Sie ein Implementierungsdetail sehen möchten. Siehe: http://stackoverflow.com/questions/4631774/coordination-parallel-execution-in-node-js/4631909#4631909. Diese Antwort wurde lange vor async.js geschrieben. Wenn Sie Code schreiben wollen, benutzen Sie async.js. Wenn Sie Versprechungen bevorzugen, verwenden Sie eine Schleife, um ein Array von Callbacks zu erstellen, dann verwenden Sie 'promise.all' – slebetman

Antwort

8

Verwenden Sie Versprechungen. Genau dafür wurde Promise.all entworfen.

Es nimmt ein Array (oder iterable) von Versprechungen, und gibt ein neues Versprechen, das aufgelöst wird, wenn alle Versprechungen des Arrays wurden gelöst. Andernfalls wird abgelehnt, wenn eine Zusage des Arrays abgelehnt wird.

function someAsyncFunction(data, resolve, reject) { 
 
    setTimeout(function() { 
 
    if(Math.random() < .05) { 
 
     // Suppose something failed 
 
     reject('Error while processing ' + data.someParam); 
 
    } else { 
 
     // Suppose the current async work completed succesfully 
 
     resolve(data.someParam); 
 
    } 
 
    }, Math.random() * 1000); 
 
} 
 

 
function foo() { 
 
    
 
    // Create an array of promises 
 
    var promises = []; 
 
    
 
    for (var i = 0; i < 10; i++) { 
 
    // Fill the array with promises which initiate some async work 
 
    promises.push(new Promise(function(resolve, reject) { 
 
     someAsyncFunction({someParam:i}, resolve, reject); 
 
    })); 
 
    } 
 
    
 
    // Return a Promise.all promise of the array 
 
    return Promise.all(promises); 
 
} 
 

 
var result = foo().then(function(results) { 
 
    console.log('All async calls completed successfully:'); 
 
    console.log(' --> ', JSON.stringify(results)); 
 
}, function(reason) { 
 
    console.log('Some async call failed:'); 
 
    console.log(' --> ', reason); 
 
});

Beachten Sie, dass die Ergebnisse werden nach der Reihenfolge der Anordnung von Versprechen gegeben werden, nicht in der Reihenfolge, dass die Versprechungen wurden aufgelöst.

2

Eine einfache Art und Weise zu tun, es wäre einen Rückruf auslösen, wenn alle Antworten sind im Array:

function foo(cb) { 
    var results = []; 

    for (var i = 0; i < 10; i++) { 
     someAsyncFunction({someParam:i}, function callback(data) { 
     results.push(data); 

     if(results.length===10){ 
      cb(results); 
     } 
     }); 
    } 

} 

foo(function(resultArr){ 
    // do whatever with array of results 
}); 

der einzige Unterschied von der Promise.all Ansatz ist die Reihenfolge der Ergebnisse nicht garantiert; aber das ist leicht mit ein paar Ergänzungen erreichbar.

+0

Reihenfolge der Ergebnisse kann mit ein wenig Sorgfalt garantiert werden. Siehe: http://stackoverflow.com/questions/4631774/coordination-parallel-execution-in-node-js/4631909#4631909 – slebetman

2

Vor langer Zeit habe ich eine sehr ähnliche Frage hier beantwortet: Coordinating parallel execution in node.js.

Allerdings haben sich die Zeiten bewegt. Seitdem ist eine wirklich gute Bibliothek erschienen und das vielversprechende Designmuster wurde vollständig erforscht und sogar in der Sprache standardisiert. Wenn Sie sehen möchten, wie es mit Rohcode gemacht werden kann, klicken Sie auf den obigen Link. Wenn Sie nur auf Code lesen wollen ..

async.js

Die async.js library haben grundsätzlich implementiert, um den Code in den obigen Link. Mit async den Code würde schreiben Sie etwas wie folgt aussehen:

var listOfAsyncFunctions = []; 

for (var i = 0; i < 10; i++) { 
    (function(n){ 
     // Construct an array of async functions with the expected 
     // function signature (one argument that is the callback). 
     listOfAsyncFunctions.push(function(callback){ 
      // Note: async expects the first argument to callback to be an error 
      someAsyncFunction({someParam:n}, function (data) { 
       callback(null,data); 
      }); 
     }) 
    })(i); // IIFE to break the closure 
} 

// Note that at this point you haven't called the async functions. 
// Pass the array to async.js and let it call them. 

async.parallel(listOfAsyncFunctions,function (err,result) { 
    console.log(result); // result will be the same order as listOfAsyncFunctions 
}); 

Allerdings haben die Autoren von async.js getan mehr als das. Async hat auch funktionale array-artige Operationen: jeweils, map, filter, reduce. Es macht asynchron Verarbeitung Arrays einfach und macht den Code leichter zu verstehen:

var listOfParams = []; 

for (var i = 0; i < 10; i++) { 
    // Construct an array of params: 
    listOfParams.push({someParam:i}); 
} 

async.map(listOfParams,someAsyncFunction,function (err,result) { 
    console.log(result); 
}); 

Eine andere Sache async gibt Ihnen verschiedene Algorithmen für ist, wie die asynchronen Aufgaben zu verarbeiten. Sagen Sie zum Beispiel, Sie möchten eine Website scrappen, aber nicht, dass sie Ihre IP-Adresse für das Spammen ihres Servers verbieten.Sie können async.series() statt parallel verwenden Sie die Aufgaben einer nach dem anderen zu verarbeiten:

// Set-up listOfAsyncFunctions as above 

async.series(listOfAsyncFunctions,function (err,result) { 
    console.log(result); // result will be the same order as listOfAsyncFunctions 
}); 

Oder wenn Sie möchten, zu einem Zeitpunkt 3 Aufgaben verarbeiten:

async. parallelLimit(listOfAsyncFunctions, 3, function (err,result) { 
    console.log(result); // result will be the same order as listOfAsyncFunctions 
}); 

Promise.all()

Die Promise.all() Methode funktioniert in ähnlicher Weise wie async.parallel() nur funktioniert es mit Versprechen statt. Sie bauen eine Reihe von Versprechungen leiten sie dann an Promise.all():

var listOfPromises = []; 

for (var i = 0; i < 10; i++) { 
    // Construct an array of promises 
    listOfPromises.push(somePromiseFunction({someParam:i})); 
} 

Promise.all(listOfPromises).then(function(result){ 
    console.log(result); 
}); 
Verwandte Themen