2017-03-09 5 views
1

Suchen Sie nach dem besten Weg, um eine Kette von Funktionen zu schreiben, die nacheinander async ausführen müssen. In Anbetracht dieser zwei Möglichkeiten:Scala-Stil mit Futures

Option 1

def operation1(): Unit = {...} 
def operation2(): Unit = {...} 

def foo(): Future[Unit] = 
    Future { 
    operation1() 
    operation2() 
    } onComplete {case _ => println("done!")} 

Option 2

def operation1(): Future[Unit] = {...} 
def operation2(): Future[Unit] = {...} 

def foo(): Future[Unit] = { 
    operation1() 
    .flatMap {case _ => operation2() } 
    .onComplete {case _ => println("done!")} 
} 
  1. Gibt es irgendwelche Vorteile/Nachteile des einen über den anderen?
  2. Ich glaube, dass die Option 1 die beiden Funktionen auf dem gleichen Hintergrund Thread ausführen wird. Gilt das auch für die Option 2?
  3. Gibt es irgendwelche guten Praktiken dafür?

eine andere Frage, da diese Funktion:

def foo: Future[A] 

wenn ich das Ergebnis Einheit werfen wollen, ist dies der beste Weg, es zu tun:

foo map { _ =>() } 

Dank!

Antwort

3

Der potentielle Vorteil Option 1 über Option 2 ist, dass es garantiert operation2 wird direkt nach operation1 laufen - wenn es nicht mit einer Ausnahme fehlschlagen hat - während in Option 2, könnten Sie haben erschöpft die Thread-Pool verfügbaren Threads, wenn die flatMap getan werden soll.

Ja, Option1 wird die Operationen in demselben Thread sicher ausführen. Option 2 versuchen, sie in zwei Threads auszuführen, solange genügend von ihnen verfügbar sind.

Sie mussten einen impliziten Ausführungskontext deklarieren oder importieren: Das bestimmt, welchen Pool Sie verwenden. Wenn Sie den Standard-Executor global importiert haben, dann ist Ihr Pool ein Fork-Join-Based-Server mit - standardmäßig - so vielen Threads wie Cores, die Sie verwenden.

Die erste Option besteht darin, dass ein Thread beide Operationen nacheinander ausführt, während die zweite Option die erste Operation in einem Thread ausführt und dann versucht, einen anderen Thread von Ihrem ExecutionContext abzurufen, um die zweite Operation auszuführen.

Die beste Vorgehensweise zu verwenden, was Sie brauchen:

Haben Sie sicherstellen möchten, dass operation2 Lauf in einem Kontext, in dem keine weiteren Threads im Ausführungskontext verfügbar sind? Wenn die Antwort ja lautet, verwenden Sie Option1.Andernfalls können Sie Option2

In Bezug auf Ihre letzte Frage verwenden: Was Sie in Ihrem vorgeschlagenen Schnipsel tun nicht Gießen, Ihre zuordnen eine Funktion, die A einen Unit Wert für jeden Wert des Typs zur Verfügung stellt. Der Effekt ist, dass Sie eine Zukunft vom Typ Unit erhalten, die nützlich ist, um den Abschlusszustand zu überprüfen. Das ist der beste Weg, um das zu bekommen, was Sie zu haben scheinen.

Beachten Sie jedoch, dass die Ausführung dieser "Transformationsfunktion" wie in flatMap in einem anderen Thread ausgeführt wird, der vom impliziten Executor in Ihrem Kontext bereitgestellt wird. Deshalb hat map auch einen impliziten Parameter executor:

def map[S](f: (T) ⇒ S)(implicit executor: ExecutionContext): Future[S]