Recherche…


Créer un avenir

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
    }
}

Tout simplement, la méthode de la divide crée un futur qui se résoudra avec le quotient d' a over b .

Consommer un avenir prospère

La manière la plus simple de consommer un futur réussi - ou plutôt d'obtenir la valeur dans le futur - consiste à utiliser la méthode de la map . Supposons qu'un code appelle la méthode de divide de l'objet FutureDivider de l'exemple "Creating a Future". À quoi le code devrait-il ressembler pour obtenir le quotient d' a over b ?

object Calculator {
    def calculateAndReport(a: Int, b: Int) = {
        val eventualQuotient = FutureDivider divide(a, b)
        
        eventualQuotient map {
            quotient => println(quotient)
        }
    }
}

Consommer un avenir défaillant

Parfois, le calcul dans un avenir peut créer une exception, ce qui entraînera l'échec du futur. Dans l'exemple "Creating a Future", que se passe-t-il si le code d'appel passe 55 et 0 à la méthode de divide ? Il lancerait une ArithmeticException après avoir essayé de diviser par zéro, bien sûr. Comment cela serait-il géré en consommant du code? Il y a en fait une poignée de manières de traiter les échecs.

Gérer l'exception avec la recover et la correspondance de modèle.

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}")
        }
    }
}

Traitez l'exception avec la projection failed , où l'exception devient la valeur du futur:

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}")
        }
    }
}

Assembler l'avenir

Les exemples précédents ont mis en évidence les caractéristiques individuelles d’un futur, la gestion des cas de réussite et d’échec. Cependant, les deux fonctionnalités sont généralement beaucoup plus complexes. Voici l'exemple, écrit de manière plus nette et plus réaliste:

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}")
        }
    }
}

Séquençage et traversée des Futures

Dans certains cas, il est nécessaire de calculer un montant variable de valeurs sur des contrats à terme séparés. Supposons avoir une List[Future[Int]] , mais au lieu de cela, une List[Int] doit être traitée. Ensuite, la question est de savoir comment transformer cette instance de List[Future[Int]] en Future[List[Int]] . Pour cela, il existe la méthode de sequence sur l'objet compagnon Future .

def listOfFuture: List[Future[Int]] = List(1,2,3).map(Future(_))
def futureOfList: Future[List[Int]] = Future.sequence(listOfFuture)

En général, la sequence est un opérateur communément connu dans le monde de la programmation fonctionnelle qui transforme F[G[T]] en G[F[T]] avec des restrictions sur F et G

Il existe un autre opérateur appelé traverse , qui fonctionne de manière similaire, mais prend une fonction comme argument supplémentaire. Avec la fonction d'identité x => x le paramètre se comporte comme l'opérateur de sequence .

def listOfFuture: List[Future[Int]] = List(1,2,3).map(Future(_))
def futureOfList: Future[List[Int]] = Future.traverse(listOfFuture)(x => x)

Cependant, l'argument supplémentaire permet de modifier chaque future instance dans la liste donnée listOfFuture . De plus, le premier argument n'a pas besoin d'être une liste de Future . Il est donc possible de transformer l'exemple comme suit:

def futureOfList: Future[List[Int]] = Future.traverse(List(1,2,3))(Future(_))

Dans ce cas, la List(1,2,3) est transmise directement en tant que premier argument et la fonction d'identité x => x est remplacée par la fonction Future(_) pour envelopper de la même manière chaque valeur Int dans un Future . Un avantage de ceci est que la List[Future[Int]] intermédiaire List[Future[Int]] peut être omise pour améliorer les performances.

Combiner plusieurs contrats à terme - Pour la compréhension

Le pour la compréhension est un moyen compact d'exécuter un bloc de code qui dépend du résultat réussi de plusieurs contrats à terme.

Avec f1, f2, f3 trois Future[String] qui contiendront les chaînes one, two, three respectivement,

val fCombined = 
    for {
        s1 <- f1
        s2 <- f2
        s3 <- f3
    } yield (s"$s1 - $s2 - $s3")

fCombined sera un Future[String] contenant la chaîne one - two - three une fois que tous les futurs auront été exécutés avec succès.

Notez qu'un ExectionContext implicite est supposé ici.

De plus, gardez à l'esprit que la compréhension n'est qu'un sucre syntaxique pour une méthode flatMap, de sorte que la construction d'objets futurs pour body éliminerait l'exécution simultanée de blocs de code entourés de futurs et aboutirait à un code séquentiel. Vous le voyez sur l'exemple:

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

La valeur entourée par l'objet result1 serait toujours négative alors que result2 serait positif.

Pour plus de détails sur la compréhension et le yield en général, voir http://docs.scala-lang.org/tutorials/FAQ/yield.html



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow