The andThen
meaning I learned from this answer is a function composer.
Say that
f andThen g andThen h
will equal to
h(g(f(x)))
This implies the h function
will receive input from g(f(x))
But for the andThen
in Future
, all the closure of the following andThen always receives the result from the original Future
.
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
}
compare to
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)
What is the reason to make Future::andThen(s) receive all the same result from original Future instead of chaining Future? I've observed that these chained andThen will be executed sequentially, so the reason may not be for parallel purpose.
scala.concurrent.Future
is designed as compromise of two asynchronous approaches:
- Object-oriented observer which allows binding of asynchronous handlers
- Functional monad which offers rich functional composition capabilities.
Reading Future.andThen
's docs:
Applies the side-effecting function to the result of this future, and
returns a new future with the result of this future.
So andThen
is most likely from OOP universe. To gain similar similar result to Function1.andThen
you could use map
method :
Future(1).map {_ * 2}.map {_ * 2}
andThen
differs from onComplete
with one little thing: resulting Future of andThen
still returning same result, but will wait until supplied observer will return or throw something. That's why there is written in the docs:
This method allows one to enforce that the callbacks are executed in a
specified order.
Also note third line from docs:
Note that if one of the chained andThen callbacks throws an exception,
that exception is not propagated to the subsequent andThen callbacks.
Instead, the subsequent andThen callbacks are given the original value
of this future.
So it' completely do nothing with new Future
's result. Could not even spoil it with it's ownt exception. This andThen
and onComplete
just sequential and parallel binding of observers.
Let me sum up this nice discussion.
Say, we have tf: Future[T] =...
, and two functions, f: T => U
and g: U => V
We can do vf: Future[V] = tf map f map g
, same asvf: Future[V] = tf map (f andThen g)
In another use case, having fp: PartialFunction[T, U]
and gp: PartialFunction[U, V]
,
we can run tf1: Future[T] = tf andThen fp andThen gp
- these partial functions will be called on the value that tf produces, with no outside effect - only side effects happen. This sequence waits for fp
before calling gp
.
Yet another future operation, onComplete, works like this: having f: Try[T] => U
, the call tf onComplete f
will call f
even if the future ended with an error; the result of tf onComplete f
is of type Unit
.
Also, if your function f
produces a Future
, you will need to use flatMap
.