Ich habe Probleme bei der Verwendung von async in einem meiner Integrationstests, bei denen Ereignishandler beteiligt sind. Das Problem besteht darin, dass der Ereignishandler (mit einem Verweis auf einen asynchron bereitgestellten Rückruf) eingerichtet werden muss, bevor das Ereignis ausgegeben wird. Der Ereignishandlercode benötigt jedoch sofort einen zusätzlichen Rückruf.
Hier ist ein vereinfachtes Beispiel, das das Problem zeigt:
async.auto({
// This needs to run first, but it will finish second
event: [(next) => {
emitter.once('awesome', next);
}],
// This needs to finish near the end
checkResults: ['event', (results, next) => {
assert(results.event.status == 200);
assert(results.event.msg == 'cool');
// Do other async stuff...
somethingAsync(next);
}],
// This has to start second, but before the first one finishes
emit: ['event', (results, next) => {
event.emit('awesome', {msg: 'cool'}, next);
}],
checkEmit: ['emit', (results, next) => {
// some sort of check that can be async
},
], done);
event
muss zuerst beginnen, wird aber nicht enden, bis emit
auftritt. emit
muss warten, bis event
gestartet, aber nicht beendet wird (auch bekannt als das Einrichten des Handlers). Somit wird dies nicht beendet.
Gibt es eine Möglichkeit, dies mit der aktuellen asynchronen Bibliothek zu tun? (auf saubere Weise)
Hier ist eine Lösung, die ich gerne hätte, vorausgesetzt, dies könnte asynchron implementiert werden
async.auto({
listen: [(next, done) => {
client.on(done);
return next();
},
...
}, callback);
Ich könnte parallel die Emitter und die Listener (von denen es mehrere in einem Test geben kann) als Array von Aufgaben verwenden. Nehmen Sie die Ergebnisse und führen Sie die Überprüfungen als zweiten Teil durch. Technisch ist jedoch keine Parallelität erforderlich, um die Aufgaben der Reihe nach zu starten (obwohl dies wahrscheinlich der Fall ist). Außerdem wird der Code weniger flach, insbesondere in komplizierteren Setups.
Hallo @Saevon , danke für die Frage!
Ein schneller und sauberer Weg, dies zu tun, wäre:
async.auto({
// This needs to finish near the end
checkResults: [(next) => {
return next(null, (results, next) => {
assert(results.event.status == 200);
assert(results.event.msg == 'cool');
// Do other async stuff...
// somethingAsync(next);
});
}],
// This needs to run first, but it will finish second
event: ['checkResults', (handler, next) => {
emitter.once('awesome', handler.checkResults);
return next();
}],
// This has to start second, but before the first one finishes
emit: ['event', (results, next) => {
// Should this be emitter.emit instead of event.emit?
event.emit('awesome', {msg: 'cool'}, next);
}],
checkEmit: ['emit', (results, next) => {
// the results of `checkResults` will be in `results.emit`
// as the handler on 'awesome' was passed the `next`
// callback from `emit`
// some sort of check that can be async
yourChecks(next);
}]
}, function done(err) {
// everything finished running, unless `err` !== `null`
});
Im Wesentlichen vertauschen Sie nur die Abhängigkeiten event
und checkResults
, was tatsächlich etwas sauberer sein könnte, da event
vom Handler in checkResults
abhängt. checkResults
übergibt jetzt nur den Handler an event
.
Die Ausführungsreihenfolge wäre:
checkResults --> event --> emit --> handler (passed to event from checkResults) --> checkEmit --> done
.
Lassen Sie es mich wissen, wenn etwas unklar ist.
ja, das löst das Problem. Vielen Dank!
Ich denke, das ist übrigens der verrückteste Auto-Use-Case, den ich gesehen habe :building_construction:
Einverstanden. Normalerweise passen asynchrone Callbacks und Ereignisemitter nicht gut zusammen...
Hilfreichster Kommentar
Ich denke, das ist übrigens der verrückteste Auto-Use-Case, den ich gesehen habe :building_construction: