Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala futures and `andThen` exception propagation

I'm reading Scala 2.11.8 documentation of andThen function in the scala.concurrent.Future module and it says the following:

def andThen[U](pf: PartialFunction[Try[T], U])
              (implicit executor: ExecutionContext): Future[T]

Applies the side-effecting function to the result of this future, and returns a new future with the result of this future.

This method allows one to enforce that the callbacks are executed in a specified order.

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.

I'm not sure what does it exactly mean that exceptions are not propagated by andThen and there's also no example provided. For example if I do something like this:

Future.successful { throw new RuntimeException("test") } andThen
                  { case _ => println("test") }

In Scala REPL I get:

java.lang.RuntimeException: test
  ... 32 elided

So the exception was propagated. Could someone please provide a meaningful example what does that exactly means and whether it is safe using andThen with code that my throw exceptions which I would like to recover from. Thank you.

like image 670
flojdek Avatar asked Oct 18 '16 17:10

flojdek


People also ask

What is the difference between a Java Future and a Scala Future?

A Java Future works in a synchronous blocking way. It does not work in an asynchronous non-blocking way, whereas a Scala Future works in an asynchronous non-blocking way. If we want an asynchronous non-blocking feature, we should use Java 8's CompletableFuture.

How do Scala futures work?

Future represents a result of an asynchronous computation that may or may not be available yet. When we create a new Future, Scala spawns a new thread and executes its code. Once the execution is finished, the result of the computation (value or exception) will be assigned to the Future.

How do you handle Future Scala?

Handle the result of a future with methods like onComplete , or combinator methods like map , flatMap , filter , andThen , etc. The value in a Future is always an instance of one of the Try types: Success or Failure.

What is Future and promise in Scala?

The Promise is a writable, single-assignment container that completes a Future. The Promise is similar to the Future. However, the Future is about the read-side of an asynchronous operation, while the Promise is about the write-side.


2 Answers

Don't throw exception in Future.successful {}.

here is the correct way to do it

Future { throw new RuntimeException("test") } andThen
                  { case _ => println("test") }

You can understand the use of andThen using the following line of code

Future.successful { 1 } andThen { case _ =>  "foo" }

REPL

@ Future.successful { 1 } andThen { case _ =>  "foo" }
res7: Future[Int] = Success(1)

REPL

@ Future.successful { 1 } andThen { case _ =>  println("foo") }
foo
res8: Future[Int] = Success(1)

REPL

@ val result = Future.successful { 1 } andThen { case _ =>  "foo" }
result: Future[Int] = Success(1)

In the above examples

We can see that partial function after andthen is executed but partial function return type is ignored. Finally the resultant output is result of the Future which is the Future[Int]

This means addThen is used for executing side effecting function just after the Future is completed.

When Future is a Failure

REPL

@ val result = Future { throw new Exception("bar") } andThen { case _ =>  "foo" }
result: Future[Nothing] = Failure(java.lang.Exception: bar)

REPL

@ val result = Future { throw new Exception("bar") } andThen { case _ =>  println("foo") }
foo
result: Future[Nothing] = Failure(java.lang.Exception: bar)

Same is the case when future is a failure. Code after andThen executes and but the result of the code after andThen is ignored and final result is the Future result.

So andThen is used for running side effecting code as soon as Future completes. andThen also keeps the final output as Futures output.

This is how andThen is implemented in Standard library.

andThen resides inside the Future class

 def andThen[U](pf: PartialFunction[Try[T], U])(implicit executor: ExecutionContext): Future[T] = {
    val p = Promise[T]()
    onComplete {
      case r => try pf.applyOrElse[Try[T], Any](r, Predef.conforms[Try[T]]) finally p complete r
    }
    p.future
  }

1) Applies the side-effecting function to the result of this future, and returns a new future with the result of this future.

Yes

pf is the side effecting code because its output type is not used (cannot be used). p.future is the new future he is talking about. Promise is completed with the previous Future result (look at the implementation of addThen above)

inside the finally block p complete r means new Future is created using p.future and it is completed using the previous future result which is r

2) This method allows one to enforce that the callbacks are executed in a specified order.

Yes. You can chain multiple callbacks using multiple andThen calls and these callbacks are executed one after another in the order of andThen calls. This is compared to the onComplete methods that you can use it multiple times to register multiple callbacks, but the order of these callbacks is uncertain.

3) 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.

Yes

r which is the result of the previous future is given to pf (look at the andThen code above)

like image 72
pamu Avatar answered Sep 28 '22 09:09

pamu


I think the type signature is the best documentation. As you see, andThen accepts T => U (omitted PF and Try for simplicity), and gives back Future[T]. So you can think of it, that andThen executes some effect and returning original future. So if you partial function rises the exception, it will not be propagated to other andThens and they will act on the original future:

  import scala.concurrent.ExecutionContext.Implicits.global
  Future{ 2 } andThen {
    case _ => throw new RuntimeException("test")
  } andThen {
    case v ⇒ println("After exception"); println(v)
  }
Thread.sleep(500)

this prints:

java.lang.RuntimeException: test    
After exception
Success(2)

PS. Read through your example once again. I think you just forgot Thread.sleep in the end to make the future complete before the end of the program. So you probably understood everything correctly.

like image 36
I See Voices Avatar answered Sep 28 '22 09:09

I See Voices