web-dev-qa-db-fra.com

Pourquoi l'andThen of Future n'enchaine-t-il pas le résultat?

La andThen signification que j’ai tirée de cette answer est un composeur de fonctions.

Dis ça 

f andThen g andThen h

sera égal à

h(g(f(x)))

Cela implique que le h function recevra les entrées de g(f(x))

Sauf pour la andThen dans Future, toute la fermeture de ce qui suit et puis reçoit toujours le résultat de la Future originale.

Future{
    1
}.andThen{ case Success(x) =>
    println(x) // print 1
    Thread.sleep(2000)
    x * 2
}.andThen{ case Success(x) =>
    println(x) // print 1
    Thread.sleep(2000)
    x * 2
}

comparer aux

val func: Function1[Int, Int] = { x: Int =>
  x
}.andThen { y =>
  println(y) // print 1
  y * 2
}.andThen { z =>
  println(z) // print 2
  z * 2
}
func(1)

Quelle est la raison pour que Future :: andThen (s) reçoive le même résultat de Future original au lieu de chaîner Future? J'ai observé que ces chaînes et puis seront exécutés de manière séquentielle, donc la raison peut ne pas être à des fins parallèles.

17
Chen OT

scala.concurrent.Future est conçu comme un compromis entre deux approches asynchrones:

  1. Orienté objet observateur qui permet la liaison de gestionnaires asynchrones
  2. Fonctionnel monade qui offre de riches capacités de composition fonctionnelle.

Lecture de la documentation de Future.andThen :

Applique la fonction d'effet secondaire au résultat de cet avenir, et renvoie un nouvel avenir avec le résultat de cet avenir.

Donc, andThen provient probablement de l’univers OOP. Pour obtenir un résultat similaire à Function1.andThen, vous pouvez utiliser map method:

Future(1).map {_ * 2}.map {_ * 2}

andThen diffère de onComplete avec une petite chose: l’avenir résultant de andThen retournant toujours le même résultat, mais attendra que l’observateur fourni revienne ou jette quelque chose. C'est pourquoi il est écrit dans la documentation:

Cette méthode permet de faire en sorte que les rappels soient exécutés dans un fichier ordre spécifié.

Notez également la troisième ligne de docs:

Notez que si l'un des rappels chaînés et Puis lève une exception, cette exception n'est pas propagée aux rappels ultérieurs, puis .. ... Au lieu de cela, les callbacks suivants etThen reçoivent la valeur d'origine de cet avenir.

Donc, il ne fait absolument rien avec le nouveau résultat Future. Ne pouvait même pas le gâter avec son exception. Ceci andThen et onComplete juste une liaison séquentielle et parallèle des observateurs.

21
Odomontois

Permettez-moi de résumer cette belle discussion.

Disons que nous avons tf: Future[T] =... et deux fonctions, f: T => U et g: U => V

Nous pouvons faire vf: Future[V] = tf map f map g, identique à vf: Future[V] = tf map (f andThen g)

Dans un autre cas d'utilisation, avec fp: PartialFunction[T, U] et gp: PartialFunction[U, V], , Nous pouvons exécuter tf1: Future[T] = tf andThen fp andThen gp - ces fonctions partielles seront appelées sur la valeur produite par tf, sans effet extérieur - seuls les effets secondaires se produisent. Cette séquence attend fp avant d'appeler gp.

Encore une autre opération future, onComplete, fonctionne comme ceci: ayant f: Try[T] => U, l'appel tf onComplete f appellera f même si l'avenir se terminait par une erreur; le résultat de tf onComplete f est de type Unit.

De plus, si votre fonction f produit une Future, vous devrez utiliser flatMap.

1
Vlad Patryshev