web-dev-qa-db-fra.com

Accès à la valeur retournée par scala futures

Je suis un débutant pour les contrats à terme scala et j'ai un doute concernant la valeur de retour des contrats à terme scala.

Donc, généralement la syntaxe d'un futur scala future est

 def downloadPage(url: URL) = Future[List[Int]] {

 }

Je veux savoir comment accéder à List[Int] à partir d'une autre méthode qui appelle cette méthode.

En d'autres termes,

val result = downloadPage("localhost") 

alors quelle devrait être l'approche pour obtenir List[Int] hors du futur?

J'ai essayé d'utiliser la méthode de la carte mais je n'ai pas réussi à le faire. "

42
user1822249

Le cas de Success (listInt) => Je veux retourner le listInt et je ne suis pas en mesure de comprendre comment le faire.

La meilleure pratique est que vous ne renvoyez pas la valeur. Au lieu de cela, vous passez simplement l'avenir (ou une version transformée avec map, flatMap, etc.) à tous ceux qui ont besoin de cette valeur et ils peuvent ajouter leur propre onComplete.

Si vous devez vraiment le renvoyer (par exemple lors de l'implémentation d'une méthode héritée), alors la seule chose que vous pouvez faire est de bloquer (par exemple avec Await.result ) et vous devez décider combien de temps attendre.

25
Alexey Romanov

Vous devez attendre que l'avenir se termine pour obtenir le résultat étant donné un certain laps de temps, voici quelque chose qui fonctionnerait:

  import scala.concurrent.duration._

  def downloadPage(url: URL) = Future[List[Int]] {
    List(1,2,3)
  }

  val result = downloadPage("localhost")

  val myListInt = result.result(10 seconds)

Idéalement, si vous utilisez un Future, vous ne voulez pas bloquer le thread d'exécution, vous devez donc déplacer votre logique qui traite du résultat de votre Future dans le onComplete méthode, quelque chose comme ceci:

  result.onComplete({
    case Success(listInt) => {
      //Do something with my list
    }
    case Failure(exception) => {
      //Do something with my error
    }
  })
12
Noah

J'espère que vous avez déjà résolu ce problème depuis sa demande en 2013, mais peut-être que ma réponse peut aider quelqu'un d'autre:

Si vous utilisez Play Framework, il prend en charge les actions asynchrones (en fait, toutes les actions sont asynchrones à l'intérieur). Un moyen simple de créer une action asynchrone consiste à utiliser Action.async(). Vous devez fournir un Future[Result]à cette fonction.

Vous pouvez maintenant effectuer des transformations à partir de votre Future[List[Int]] à Future[Result] en utilisant la carte de Scala, flatMap, for-comprehension ou async/wait. Voici un exemple tiré de la documentation Play Framework.

import play.api.libs.concurrent.Execution.Implicits.defaultContext

def index = Action.async {
  val futureInt = scala.concurrent.Future { intensiveComputation() }
  futureInt.map(i => Ok("Got result: " + i))
}
6
jfuentes

Vous pouvez faire quelque chose comme ça. Si le temps d'attente indiqué en Await.result la méthode est inférieure à la durée d'exécution de awaitable, vous disposerez d'un TimeoutException et vous devrez gérer l'erreur (ou toute autre erreur).

import scala.concurrent._
import ExecutionContext.Implicits.global
import scala.util.{Try, Success, Failure}
import scala.concurrent.duration._

object MyObject {
    def main(args: Array[String]) {

        val myVal: Future[String] = Future { silly() }

        // values less than 5 seconds will go to 
        // Failure case, because silly() will not be done yet
        Try(Await.result(myVal, 10 seconds)) match {
            case Success(extractedVal) => { println("Success Happened: " + extractedVal) }
            case Failure(_) => { println("Failure Happened") }
            case _ => { println("Very Strange") }
        }      
    }

    def silly(): String = {
        Thread.sleep(5000)
        "Hello from silly"
        }
}
5
Akavall

La meilleure façon que j'ai trouvée de penser à un avenir est une boîte qui, à un moment donné, contiendra la chose que vous voulez. L'essentiel avec un avenir est que vous n'ouvrez jamais la boîte. Essayer de forcer l'ouverture de la boîte vous mènera au blocage et au chagrin. Au lieu de cela, vous placez l'avenir dans une autre boîte plus grande, généralement en utilisant la méthode de la carte.

Voici un exemple d'un avenir qui contient une chaîne. Une fois le futur terminé, Console.println est appelé:

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

object Main {

  def main(args:Array[String]) : Unit = {
    val stringFuture: Future[String] = Future.successful("hello world!")
    stringFuture.map {
      someString =>
        // if you use .foreach you avoid creating an extra Future, but we are proving
        // the concept here...
        Console.println(someString)
    }
  }
}

Notez que dans ce cas, nous appelons la méthode principale et ensuite… finissons. L'avenir de la chaîne, fourni par le Global ExecutionContext, fait le travail d'appeler Console.println. C'est génial, car lorsque nous abandonnons le contrôle sur le moment où someString va être là et quand Console.println va être appelé, nous laissons le système se gérer lui-même. En contraste, regardez ce qui se passe lorsque nous essayons de forcer l'ouverture de la boîte:

val stringFuture: Future[String] = Future.successful("hello world!")
val someString = Future.await(stringFuture)

Dans ce cas, nous devons attendre - garder un fil qui tourne ses pouces - jusqu'à ce que nous récupérions une chaîne. Nous avons ouvert la boîte, mais nous avons dû réquisitionner les ressources du système pour y accéder.

3
Priyank Desai