2

We build an android application with Scala 2.11. We use scala.concurrent.Future for async background tasks. The problem is, that we do not see any exceptions in logcat if exceptions are thrown inside a Future block.

We already create an execution context with our own reporter:

lazy val reporter: (Throwable => Unit) = {
    t =>
      t.printStackTrace()
  }

  implicit lazy val exec = ExecutionContext.fromExecutor(
    new ThreadPoolExecutor(10, 100, 5, TimeUnit.MINUTES,
      new LinkedBlockingQueue[Runnable]), reporter)

Even if I set a breakpoint inside the reporter the debugger does never stop here, even if I force throwing of exceptions insde a Future {...} block. What are we doing wrong

Chris W.
  • 2,266
  • 20
  • 40
  • 1
    Any exceptions in Future transforms into Failure, have you tried onComplete/onFailure callbacks? – 4lex1v Jun 27 '14 at 11:31
  • I tried to manually wrap the block inside the Future{..} with a Try(). Then I am able to do a pattern match and print something in case of a failure, but the problem is, that I have to do this in every place where I use an async future block. Taking a look at scala.concurrent.Promise I would expect at least a report to System.err: try executor.execute(this) catch { case NonFatal(t) => executor reportFailure t } – Chris W. Jun 27 '14 at 11:37
  • That's redundant, considering `Future` is is basically a `Try`. And it catches the exceptions to `Failure`. You haven't posted any relevant code to the `Future`, so we can't recommend the correct way to handle it, other than saying *don't do that*. `Try` doesn't log caught exceptions: http://stackoverflow.com/questions/24394291/scala-try-with-exception-logging – Michael Zajac Jun 27 '14 at 11:41
  • Setting a breakpoint in Promise gives me a failure: Failure(java.lang.IllegalArgumentException: keys must match regex [a-z0-9_-]{1,64}: "userAvatar"), so I would expect the reporter gets his chance to handle the exception and log it to the logcat console – Chris W. Jun 27 '14 at 11:43
  • @LimbSoup : I know that's redundant (or better a different concept. Nobody should prohibit someone else from materializing exceptions in this way: Future[Try[V]]), it was just a try to see that I can log to the console there and see the exception. – Chris W. Jun 27 '14 at 11:46
  • @longliveenduro If you want materialize an error in the Future, better use scala `Either` or scalaz `\/` and then onSuccess callback -> Future[Error \/ Result] – 4lex1v Jun 27 '14 at 11:49

1 Answers1

3

According to your comment it looks like you simply didn't work with Future as needed. When some exception occurred during the Future computation it is transformed into Failure case (like Failure from Try, but in async context), e.g:

scala> Future { 10 / 0 }
res21: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@24f3ffd

As you can see there is no exception thrown or printed. To handle this exception you need to use callbacks, i.e onComplete or onFailure, e.g:

scala> res21.onFailure { case error => println(s"Error: ${ error.getMessage }") }
Error: / by zero

A great intro into Futures and Duality was given by the man with psychedelic T-Shirt Erik Meijer in the coursers intro to Reactive Programming.

4lex1v
  • 21,367
  • 6
  • 52
  • 86
  • You're right, got the point. So I'm nearer to solve what I want. Is there any possibility to always create a Future{...} block with an default onFailure handler? (e.g. write the stacktrace to the console)? With that I wouldn't have to repeat always the same "last resort" exception logging code? – Chris W. Jun 27 '14 at 12:21
  • @longliveenduro You can make a function with a default handler for console output. Remember that you can "attach" any number of callbacks, so nothing stops you from attaching to onFailure callbacks and seven onSuccess/onComplete – 4lex1v Jun 27 '14 at 12:48
  • If I understood you correctly you suggest a def failureHandler = { case t: Throwable => t.printStackTrace() } but then I always have to remember and repeat myself: Future{...}.onFailure(failureHandler) How can I get rid of this repetition? – Chris W. Jun 27 '14 at 12:50
  • Even more worse: Every time I do a map() on the future, I do have to reinstall the default failure handler on the mapped future? – Chris W. Jun 27 '14 at 12:58
  • @longliveenduro you can make a function `def spawn[A](action: => A) = Future(action).onFailure { ... }` then make future with `spawn`. As for `map`, no, there is no need in making additional callback. – 4lex1v Jun 27 '14 at 13:03
  • Hmm...looking at Future.map implementation, there is a new Promise created and the future from this new promise does not have the desired onFailure Handler? – Chris W. Jun 27 '14 at 13:23
  • tried it out, I have to reinstall the failure handler after mapping the future to also observe the new code inside map...this is cumbersome – Chris W. Jun 27 '14 at 13:25
  • made a new question for this: http://stackoverflow.com/questions/24453080/scala-futures-default-error-handler-for-every-new-created-or-mapped-exception – Chris W. Jun 27 '14 at 13:30