3
votes

Exécuter N fois un futur Scala

J'essaye de trouver un moyen plus élégant d'exécuter 2 fois une fonction qui renvoie un Future [HttpReponse] puis d'utiliser la réponse de l'appel à 2 extrémités.

for {
    2 times response <- post(uri, payload)
} yield {
    // response becomes of type Any instead of HttpResponse
}

Ceci ne fonctionne pas:

for {
    // function post returns a Future[HttpResponse]
    response <- post(uri, payload) // 1st
    response <- post(uri, payload) // 2end
} yield {
    // do something with the 2end response
}


3 commentaires

Les 2 appels doivent-ils être exécutés simultanément ou séquentiellement?


Séquentiellement :-)


J'ai mis à jour ma réponse pour qu'elle fonctionne de manière séquentielle.


3 Réponses :


1
votes

Idéalement, vous voudriez quelque chose comme ceci:

seq(Seq.fill(n)(() => post(uri, payload)))

Cependant, si vous voulez vraiment que ce soit séquentiel, cela ne fonctionne pas, comme Future.sequence code > évalue le Stream avec empressement et démarre tous les futurs en parallèle. Il existe des solutions à ce problème ici . Par exemple, voici l'utilisateur la version de la séquence de eagle yuan qui fonctionne de manière séquentielle:

def seq[A, M[X] <: TraversableOnce[X]](in: M[() => Future[A]])(implicit cbf: CanBuildFrom[M[()=>Future[A]], A, M[A]], executor: ExecutionContext): Future[M[A]] = {
    in.foldLeft(Future.successful(cbf(in))) {
       (fr, ffa) => for (r <- fr; a <- ffa()) yield (r += a)
    } map (_.result())
}

Vous pouvez l'utiliser comme:

Future.sequence(Stream.fill(n)(post(uri, payload)))


0 commentaires

1
votes

Cela devrait être tout:

1552852972738
1552852973749
1552852973749

J'ai testé ceci avec ce code:

 val result = Seq.fill(2)(dummyFut()).last

  def dummyFut(): Future[Long] = Future.successful{
    Thread.sleep(1000L)
    println(System.currentTimeMillis())
    System.currentTimeMillis()
  }

  result.foreach(println)

Ceci s'imprime:

val result = Seq.fill(2)(post(uri, payload)).last


3 commentaires

Merci. Fait intéressant, cela fonctionne pour moi avec l'opérateur <- au lieu de = un. Mais encore, c'est ce que je cherchais. :-)


@Martin Cela ne fait pas fonctionner les contrats à terme séquentiellement. Cela ne ressemble à cela que dans l'exemple car pme utilise Future.successful , qui exécute le code fourni sur le même thread, au lieu de Future.apply . Remplacez Future.successful { par Future { et vous verrez qu'il ne s'imprime pas aux bons moments.


@BrianMcCutchon merci de l'avoir signalé - j'ai donc eu de la chance; (.



2
votes

Si vous devez faire deux appels séquentiels à une méthode qui renvoie Future , vous pouvez utiliser flatMap.

(0 to N-1).foldLeft(post(uri, payload)){
  case (prev, _) => prev.flatMap(_ => post(uri, payload))
}

Cela va ne pas démarrer la deuxième opération post tant que la première n'est pas terminée.

Si vous avez plusieurs appels en chaîne, vous pouvez utiliser foldLeft sur un Range pour appliquer ceci le nombre de fois approprié:

post(uri, payload).flatMap(_ => post(uri, payload))

En pratique, vous utiliseriez probablement la valeur de la Range pour suivre les progrès sur ce opération plutôt que de la supprimer.


0 commentaires