Scala Language
Futures
Suche…
Zukunft gestalten
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object FutureDivider {
def divide(a: Int, b: Int): Future[Int] = Future {
// Note that this is integer division.
a / b
}
}
Die divide
Methode erzeugt ganz einfach eine Zukunft, die mit dem Quotienten von a
über b
.
Eine erfolgreiche Zukunft konsumieren
Der einfachste Weg , eine erfolgreiche Future-- oder eher zu konsumieren, um den Wert innerhalb des Future-- zu erhalten ist , die verwenden map
Methode. Angenommen, ein Code ruft die divide
Methode des FutureDivider
Objekts aus dem Beispiel "Zukunft FutureDivider
" auf. Wie müsste der Code aussehen, um den Quotienten von a
über b
?
object Calculator {
def calculateAndReport(a: Int, b: Int) = {
val eventualQuotient = FutureDivider divide(a, b)
eventualQuotient map {
quotient => println(quotient)
}
}
}
Eine misslungene Zukunft konsumieren
Manchmal kann die Berechnung in einer Zukunft zu einer Ausnahme führen, wodurch die Zukunft scheitern kann. Was passiert, wenn der aufrufende Code in dem Beispiel "Zukunft erstellen" 55
und 0
an die divide
Methode übergeben hat? Es würde natürlich eine ArithmeticException
nachdem versucht wurde, durch Null zu teilen. Wie würde das mit verbrauchendem Code behandelt werden? Es gibt tatsächlich eine Reihe von Möglichkeiten, mit Fehlern umzugehen.
Behandeln Sie die Ausnahme mit recover
und Pattern Matching.
object Calculator {
def calculateAndReport(a: Int, b: Int) = {
val eventualQuotient = FutureDivider divide(a, b)
eventualQuotient recover {
case ex: ArithmeticException => println(s"It failed with: ${ex.getMessage}")
}
}
}
Behandeln Sie die Ausnahme mit der failed
Projektion, bei der die Ausnahme zum Wert der Zukunft wird:
object Calculator {
def calculateAndReport(a: Int, b: Int) = {
val eventualQuotient = FutureDivider divide(a, b)
// Note the use of the dot operator to get the failed projection and map it.
eventualQuotient.failed.map {
ex => println(s"It failed with: ${ex.getMessage}")
}
}
}
Zukunft zusammenstellen
In den vorherigen Beispielen wurden die einzelnen Merkmale einer Zukunft sowie der Umgang mit Erfolg und Misserfolgen demonstriert. In der Regel werden jedoch beide Funktionen viel strenger behandelt. Hier ist das Beispiel, das auf eine schönere und realistischere Weise geschrieben wurde:
object Calculator {
def calculateAndReport(a: Int, b: Int) = {
val eventualQuotient = FutureDivider divide(a, b)
eventualQuotient map {
quotient => println(s"Quotient: $quotient")
} recover {
case ex: ArithmeticException => println(s"It failed with: ${ex.getMessage}")
}
}
}
Sequenzierung und Durchquerung von Futures
In einigen Fällen ist es erforderlich, eine variable Anzahl von Werten auf separaten Futures zu berechnen. Angenommen, Sie haben eine List[Future[Int]]
, aber stattdessen muss eine List[Int]
verarbeitet werden. Dann stellt sich die Frage, wie diese Instanz von List[Future[Int]]
in eine Future[List[Int]]
. Zu diesem Zweck gibt es die sequence
im Begleitobjekt Future
.
def listOfFuture: List[Future[Int]] = List(1,2,3).map(Future(_))
def futureOfList: Future[List[Int]] = Future.sequence(listOfFuture)
Im Allgemeinen ist sequence
ein allgemein bekannter Operator in der Welt der funktionalen Programmierung, der F[G[T]]
in G[F[T]]
mit Einschränkungen auf F
und G
transformiert.
Es gibt einen alternativen Operator namens traverse
, der ähnlich arbeitet, aber eine Funktion als zusätzliches Argument verwendet. Mit der Identitätsfunktion x => x
als Parameter verhält es sich wie der sequence
.
def listOfFuture: List[Future[Int]] = List(1,2,3).map(Future(_))
def futureOfList: Future[List[Int]] = Future.traverse(listOfFuture)(x => x)
Das zusätzliche Argument erlaubt es jedoch, jede zukünftige Instanz innerhalb der angegebenen listOfFuture
zu ändern. Darüber hinaus muss das erste Argument keine Liste der Future
. Daher ist es möglich, das Beispiel wie folgt umzuwandeln:
def futureOfList: Future[List[Int]] = Future.traverse(List(1,2,3))(Future(_))
In diesem Fall wird die List(1,2,3)
direkt als erstes Argument übergeben und die Identitätsfunktion x => x
wird durch die Funktion Future(_)
, um jeden Int
Wert auf ähnliche Weise in einen Future
. Ein Vorteil davon ist, dass die Zwischenliste List[Future[Int]]
weggelassen werden kann, um die Leistung zu verbessern.
Kombinieren Sie mehrere Futures - zum Verständnis
Das Verständnis ist eine kompakte Art, einen Codeblock auszuführen, der vom erfolgreichen Ergebnis mehrerer Futures abhängt.
Mit f1, f2, f3
drei Future[String]
, die jeweils die Strings one, two, three
enthalten.
val fCombined =
for {
s1 <- f1
s2 <- f2
s3 <- f3
} yield (s"$s1 - $s2 - $s3")
fCombined
wird eine Future[String]
die die Zeichenfolge one - two - three
sobald alle Futures erfolgreich abgeschlossen wurden.
Beachten Sie, dass hier ein impliziter ExectionContext angenommen wird.
Denken Sie auch daran, dass das Verständnis nur ein syntaktischer Zucker für eine flatMap-Methode ist, sodass die Konstruktion zukünftiger Objekte für body die gleichzeitige Ausführung von von Futures umschlossenen Codeblöcken eliminiert und zu sequentiellem Code führt. Sie sehen es am Beispiel:
val result1 = for {
first <- Future {
Thread.sleep(2000)
System.currentTimeMillis()
}
second <- Future {
Thread.sleep(1000)
System.currentTimeMillis()
}
} yield first - second
val fut1 = Future {
Thread.sleep(2000)
System.currentTimeMillis()
}
val fut2 = Future {
Thread.sleep(1000)
System.currentTimeMillis()
}
val result2 = for {
first <- fut1
second <- fut2
} yield first - second
result1
Objekt result1
eingeschlossener result1
wäre immer negativ, während result2
positiv wäre.
Weitere Informationen zum Verständnis und zum yield
im Allgemeinen finden Sie unter http://docs.scala-lang.org/tutorials/FAQ/yield.html