2017-12-20 4 views
0

Ich habe den folgenden Test-Code-Snippet:Scala Future blockiert Transformationen?

import scala.concurrent.{Await, Future} 
import scala.concurrent.duration.Duration 
import scala.util.Success 

import scala.concurrent.ExecutionContext.Implicits.global 

object FutureAndThen extends App { 

    val future = Future { 
    println("Started initial Future") 
    10 
    } andThen { case Success(value) => 
    println("Started callback") 
    Thread.sleep(5000) 
    println(s"Finished callback: value = $value") 
    } map { x => 
    println("Chained transformation") 
    x * 2 
    } 

    println(Await.result(future, Duration.Inf)) 

} 

Es wird die folgende Ausgabe erzeugt:

Started initial Future 
Started callback 
Finished callback: value = 10 
Chained transformation 
20 

Ich erwarte, dass andThen Rückruf asynchron ausgeführt werden. Aber die tatsächliche Ausführung ist die nächste:

  1. Execute original Zukunft
  2. asynchrone Rückruf ausführen
  3. Run-Transformationen (map)

Zuerst dachte ich, dass das Problem in ExecutionContext ist die entschieden, alle diese Operationen in einem einzigen Thread auszuführen. Und ich änderte sich dies ExecutionContext Brauch zu verwenden:

implicit val ctx = ExecutionContext.fromExecutor(
    (command: Runnable) => new Thread(command).start() 
) 

Und das Ergebnis ist das gleiche. Kannst du mir raten, was ich vermisse?

Antwort

2

Dieses Verhalten ist eigentlich für Future.andThen dokumentiert:

Wendet die Neben Bewirkung Funktion auf das Ergebnis dieser Zukunft und gibt eine neue Zukunft mit dem Ergebnis dieser Zukunft.

Dieses Verfahren erlaubt es, erzwingen dass die Rückrufe in einer angegebenen Reihenfolge ausgeführt werden.

Es bedeutet, dass map nicht seine Arbeit beginnen, bevor die Berechnungen innerhalb andThen abgeschlossen sind. Wenn dies nicht das ist, was Sie wollen, müssen Sie map auf dem ursprünglichen Future anrufen. Und dann können Sie onComplete statt andThen, verwenden, so würde Code so etwas wie dieses werden:

val future = Future { 
    println("Started initial Future") 
    10 
    } 

    future onComplete { case Success(value) => 
    println("Started callback") 
    Thread.sleep(2000) 
    println(s"Finished callback: value = $value") 
    } 

    val f2 = future map { x => 
    println("Chained transformation") 
    x * 2 
    } 

    println(Await.result(f2, Duration.Inf)) 

P. S. AFAIK gibt es keinen Standard onComplete Äquivalent, die mit Methodenverkettung verwendet werden kann, und ich denke, das ist von Entwurf, um es einfacher zu machen, Verhalten vorherzusagen, indem Sie den Code lesen. Derzeit können Sie eine einfache Regel verwenden: Wenn sie verkettet ist, wird sie später ausgeführt.

+0

Vielen Dank. Genau so habe ich mein Problem gelöst. –