I want to add an after(d: FiniteDuration)(callback: => Unit) util to Scala Futures that would enable me to do this:
val f = Future(someTask)
f.after(30.seconds) {
println("f has not completed in 30 seconds!")
}
f.after(60.seconds) {
println("f has not completed in 60 seconds!")
}
How can I do this?
Usually I use a thread pool executor and promises:
import scala.concurrent.duration._
import java.util.concurrent.{Executors, ScheduledThreadPoolExecutor}
import scala.concurrent.{Future, Promise}
val f: Future[Int] = ???
val executor = new ScheduledThreadPoolExecutor(2, Executors.defaultThreadFactory(), AbortPolicy)
def withDelay[T](operation: ⇒ T)(by: FiniteDuration): Future[T] = {
val promise = Promise[T]()
executor.schedule(new Runnable {
override def run() = {
promise.complete(Try(operation))
}
}, by.length, by.unit)
promise.future
}
Future.firstCompletedOf(Seq(f, withDelay(println("still going"))(30 seconds)))
Future.firstCompletedOf(Seq(f, withDelay(println("still still going"))(60 seconds)))
One way is to use Future.firstCompletedOf (see this blogpost):
val timeoutFuture = Future { Thread.sleep(500); throw new TimeoutException }
val f = Future.firstCompletedOf(List(f, timeoutFuture))
f.map { case e: TimeoutException => println("f has not completed in 0.5 seconds!") }
where TimeoutException is some exception or type.
Use import akka.pattern.after. If you want to implement it without akka here is the source code. The other (java) example is TimeoutFuture in com.google.common.util.concurrent.
Something like this, perhaps:
object PimpMyFuture {
implicit class PimpedFuture[T](val f: Future[T]) extends AnyVal {
def after(delay: FiniteDuration)(callback: => Unit): Future[T] = {
Future {
blocking { Await.ready(f, delay) }
} recover { case _: TimeoutException => callback }
f
}
}
}
import PimpMyFuture._
Future { Thread.sleep(10000); println ("Done") }
.after(5.seconds) { println("Still going") }
This implementation is simple, but it basically doubles the number of threads you need - each active future effectively occupies two threads - which is a bit wasteful. Alternatively, you could use scheduled tasks to make your waits non-blocking. I don't know of a "standard" scheduler in scala (each lib has their own), but for a simple task like this you can use java's TimerTask directly:
object PimpMyFutureNonBlocking {
val timer = new java.util.Timer
implicit class PimpedFuture[T](val f: Future[T]) extends AnyVal {
def after(delay: FiniteDuration)(callback: => Unit): Future[T] = {
val task = new java.util.TimerTask {
def run() { if(!f.isCompleted) callback }
}
timer.schedule(task, delay.toMillis)
f.onComplete { _ => task.cancel }
f
}
}
}
Related
Trying to wrap my head around how async tasks are chained together, for ex futures and flatMap
val a: Future[Int] = Future { 123 }
val b: Future[Int] = a.flatMap(a => Future { a + 321 })
How would i implement something similar where i build a new Future by waiting for a first and applying a f on the result. I tried looking a scala's source code but got stuck here: https://github.com/scala/scala/blob/2.13.x/src/library/scala/concurrent/Future.scala#L216
i would imagine some code like:
def myFlatMap(a: Future[Int])(f: a => Future[Int]): Future[Int] = {
Future {
// somehow wait for a to complete and the apply 'f'
a.onComplete {
case Success(x) => f(a)
}
}
}
but i guess above will return a Future[Unit] instead. I would just like to understand the "pattern" of how async tasks are chained together.
(Disclaimer: I'm the main maintainer of Scala Futures)
You wouldn't want to implement flatMap/recoverWith/transformWith yourself—it is a very complex feature to implement safely (stack-safe, memory-safe, and concurrency-safe).
You can see what I am talking about here:
https://github.com/scala/scala/blob/2.13.x/src/library/scala/concurrent/impl/Promise.scala#L442
https://github.com/scala/scala/blob/2.13.x/src/library/scala/concurrent/impl/Promise.scala#L307
https://github.com/scala/scala/blob/2.13.x/src/library/scala/concurrent/impl/Promise.scala#L274
There is more reading on the topic here: https://viktorklang.com/blog/Futures-in-Scala-2.12-part-9.html
I found that looking at the source for scala 2.12 was easier for me to understand how Future / Promise was implemented, i learned that Future is in fact a Promise and by looking at the implementation i now understand how "chaining" async tasks can be implemented.
Im posting the code that i wrote to help me better understand what was going on under the hood.
import java.util.concurrent.{ScheduledThreadPoolExecutor, TimeUnit}
import scala.collection.mutable.ListBuffer
trait MyAsyncTask[T] {
type Callback = T => Unit
var result: Option[T]
// register a new callback to be called when task is completed.
def onComplete(callback: Callback): Unit
// complete the given task, and invoke all registered callbacks.
def complete(value: T): Unit
// create a new task, that will wait for the current task to complete and apply
// the provided map function, and complete the new task when completed.
def flatMap[B](f: T => MyAsyncTask[B]): MyAsyncTask[B] = {
val wrapper = new DefaultAsyncTask[B]()
onComplete { x =>
f(x).onComplete { y =>
wrapper.complete(y)
}
}
wrapper
}
def map[B](f: T => B): MyAsyncTask[B] = flatMap(x => CompletedAsyncTask(f(x)))
}
/**
* task with fixed pre-calculated result.
*/
case class CompletedAsyncTask[T](value: T) extends MyAsyncTask[T] {
override var result: Option[T] = Some(value)
override def onComplete(callback: Callback): Unit = {
// we already have the result just call the callback.
callback(value)
}
override def complete(value: T): Unit = () // noop nothing to complete.
}
class DefaultAsyncTask[T] extends MyAsyncTask[T] {
override var result: Option[T] = None
var isCompleted = false
var listeners = new ListBuffer[Callback]()
/**
* register callback, to be called when task is completed.
*/
override def onComplete(callback: Callback): Unit = {
if (isCompleted) {
// already completed just invoke callback
callback(result.get)
} else {
// add the listener
listeners.addOne(callback)
}
}
/**
* trigger all registered callbacks to `onComplete`
*/
override def complete(value: T): Unit = {
result = Some(value)
listeners.foreach { listener =>
listener(value)
}
}
}
object MyAsyncTask {
def apply[T](body: (T => Unit) => Unit): MyAsyncTask[T] = {
val task = new DefaultAsyncTask[T]()
// pass in `complete` as callback.
body(task.complete)
task
}
}
object MyAsyncFlatMap {
/**
* helper to simulate async task.
*/
def delayedCall(body: => Unit, delay: Int): Unit = {
val scheduler = new ScheduledThreadPoolExecutor(1)
val run = new Runnable {
override def run(): Unit = {
body
}
}
scheduler.schedule(run, delay, TimeUnit.SECONDS)
}
def main(args: Array[String]): Unit = {
val getAge = MyAsyncTask[Int] { cb =>
delayedCall({ cb(66) }, 1)
}
val getName = MyAsyncTask[String] { cb =>
delayedCall({ cb("John") }, 2)
}
// same as: getAge.flatMap(age => getName.map(name => (age, name)))
val result: MyAsyncTask[(Int, String)] = for {
age <- getAge
name <- getName
} yield (age, name)
result.onComplete {
case (age, name) => println(s"hello $name $age")
}
}
}
The following Scala code uses cats EitherT to wrap results in a Future[Either[ServiceError, T]]:
package com.example
import com.example.AsyncResult.AsyncResult
import cats.implicits._
import scala.concurrent.ExecutionContext.Implicits.global
class ExternalService {
def doAction(): AsyncResult[Int] = {
AsyncResult.success(2)
}
def doException(): AsyncResult[Int] = {
println("do exception")
throw new NullPointerException("run time exception")
}
}
class ExceptionExample {
private val service = new ExternalService()
def callService(): AsyncResult[Int] = {
println("start callService")
val result = for {
num <- service.doException()
} yield num
result.recoverWith {
case ex: Throwable =>
println("recovered exception")
AsyncResult.success(99)
}
}
}
object ExceptionExample extends App {
private val me = new ExceptionExample()
private val result = me.callService()
result.value.map {
case Right(value) => println(value)
case Left(error) => println(error)
}
}
AsyncResult.scala contains:
package com.example
import cats.data.EitherT
import cats.implicits._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object AsyncResult {
type AsyncResult[T] = EitherT[Future, ServiceError, T]
def apply[T](fe: => Future[Either[ServiceError, T]]): AsyncResult[T] = EitherT(fe)
def apply[T](either: Either[ServiceError, T]): AsyncResult[T] = EitherT.fromEither[Future](either)
def success[T](res: => T): AsyncResult[T] = EitherT.rightT[Future, ServiceError](res)
def error[T](error: ServiceError): AsyncResult[T] = EitherT.leftT[Future, T](error)
def futureSuccess[T](fres: => Future[T]): AsyncResult[T] = AsyncResult.apply(fres.map(res => Right(res)))
def expectTrue(cond: => Boolean, err: => ServiceError): AsyncResult[Boolean] = EitherT.cond[Future](cond, true, err)
def expectFalse(cond: => Boolean, err: => ServiceError): AsyncResult[Boolean] = EitherT.cond[Future](cond, false, err)
}
ServiceError.scala contains:
package com.example
sealed trait ServiceError {
val detail: String
}
In ExceptionExample, if it call service.doAction() it prints 2 as expected, but if it call service.doException() it throws an exception, but I expected it to print "recovered exception" and "99".
How do I recover from the exception correctly?
That is because doException is throwing exception inline. If you want to use Either, you have to return Future(Left(exception)) rather than throwing it.
I think, you are kinda overthinking this. It does not look like you need Either here ... or cats for that matter.
Why not do something simple, like this:
class ExternalService {
def doAction(): Future[Int] = Future.successful(2)
def doException(): AsyncResult[Int] = {
println("do exception")
Future.failed(NullPointerException("run time exception"))
// alternatively: Future { throw new NullPointerExceptioN() }
}
class ExceptionExample {
private val service = new ExternalService()
def callService(): AsyncResult[Int] = {
println("start callService")
val result = for {
num <- service.doException()
} yield num
// Note: the aboive is equivalent to just
// val result = service.doException
// You can write it as a chain without even needing a variable:
// service.doException.recover { ... }
result.recover { case ex: Throwable =>
println("recovered exception")
Future.successful(99)
}
}
I tend to agree that it seems a bit convoluted, but for the sake of the exercise, I believe there are a couple of things that don't quite click.
The first one is the fact that you are throwing the Exception instead of capturing it as part of the semantics of Future. ie. You should change your method doException from:
def doException(): AsyncResult[Int] = {
println("do exception")
throw new NullPointerException("run time exception")
}
To:
def doException(): AsyncResult[Int] = {
println("do exception")
AsyncResult(Future.failed(new NullPointerException("run time exception")))
}
The second bit that is not quite right, would be the recovery of the Exception. When you call recoverWith on an EitherT, you're defining a partial function from the Left of the EitherT to another EitherT. In your case, that'd be:
ServiceError => AsyncResult[Int]
If what you want is to recover the failed future, I think you'll need to explicitly recover on it. Something like:
AsyncResult {
result.value.recover {
case _: Throwable => {
println("recovered exception")
Right(99)
}
}
}
If you really want to use recoverWith, then you could write this instead:
AsyncResult {
result.value.recoverWith {
case _: Throwable =>
println("recovered exception")
Future.successful(Right(99))
}
}
Suppose I have the following code:
val futureInt1 = getIntAsync1();
val futureInt2 = getIntAsync2();
val futureSum = for {
int1 <- futureInt1
int2 <- futureInt2
} yield (int1 + int2)
val sum = Await.result(futureSum, 60 seconds)
Now suppose one of getIntAsync1 or getIntAsync2 takes a very long time, and it leads to Await.result throwing exception:
Caused by: java.util.concurrent.TimeoutException: Futures timed out after [60 seconds]
How am I supposed to know which one of getIntAsync1 or getIntAsync2 was still pending and actually lead to the timeout?
Note that here I'm merging 2 futures with zip, and this is a simple example for the question, but in my app I have this kind of code at different level (ie getIntAsync1 itself can use Future.zip or Future.sequence, map/flatMap/applicative)
Somehow what I'd like is to be able to log the pending concurrent operation stacktraces when a timeout occur on my main thread, so that I can know where are the bottlenexts on my system.
I have an existing legacy API backend which is not fully reactive yet and won't be so soon. I'm trying to increase response times by using concurrency. But since using this kind of code, It's become way more painful to understand why something takes a lot of time in my app. I would appreciate any tip you can provide to help me debugging such issues.
The key is realizing is that a the Future doesn't time out in your example—it's your calling thread which pauses for at most X time.
So, if you want to model time in your Futures you should use zipWith on each branch and zip with a Future which will contain a value within a certain amount of time. If you use Akka then you can use akka.pattern.after for this, together with Future.firstCompletedOf.
Now, even if you do, how do you figure out why any of your futures weren't completed in time, perhaps they depended on other futures which didn't complete.
The question boils down to: Are you trying to do some root-cause analysis on throughput? Then you should monitor your ExecutionContext, not your Futures. Futures are only values.
The proposed solution wraps each future from for block into TimelyFuture which requires timeout and name. Internally it uses Await to detect individual timeouts.
Please bear in mind this style of using futures is not intended for production code as it uses blocking. It is for diagnostics only to find out which futures take time to complete.
package xxx
import java.util.concurrent.TimeoutException
import scala.concurrent.{Future, _}
import scala.concurrent.duration.Duration
import scala.util._
import scala.concurrent.duration._
class TimelyFuture[T](f: Future[T], name: String, duration: Duration) extends Future[T] {
override def onComplete[U](ff: (Try[T]) => U)(implicit executor: ExecutionContext): Unit = f.onComplete(x => ff(x))
override def isCompleted: Boolean = f.isCompleted
override def value: Option[Try[T]] = f.value
#scala.throws[InterruptedException](classOf[InterruptedException])
#scala.throws[TimeoutException](classOf[TimeoutException])
override def ready(atMost: Duration)(implicit permit: CanAwait): TimelyFuture.this.type = {
Try(f.ready(atMost)(permit)) match {
case Success(v) => this
case Failure(e) => this
}
}
#scala.throws[Exception](classOf[Exception])
override def result(atMost: Duration)(implicit permit: CanAwait): T = {
f.result(atMost)
}
override def transform[S](ff: (Try[T]) => Try[S])(implicit executor: ExecutionContext): Future[S] = {
val p = Promise[S]()
Try(Await.result(f, duration)) match {
case s#Success(_) => ff(s) match {
case Success(v) => p.success(v)
case Failure(e) => p.failure(e)
}
case Failure(e) => e match {
case e: TimeoutException => p.failure(new RuntimeException(s"future ${name} has timed out after ${duration}"))
case _ => p.failure(e)
}
}
p.future
}
override def transformWith[S](ff: (Try[T]) => Future[S])(implicit executor: ExecutionContext): Future[S] = {
val p = Promise[S]()
Try(Await.result(f, duration)) match {
case s#Success(_) => ff(s).onComplete({
case Success(v) => p.success(v)
case Failure(e) => p.failure(e)
})
case Failure(e) => e match {
case e: TimeoutException => p.failure(new RuntimeException(s"future ${name} has timed out after ${duration}"))
case _ => p.failure(e)
}
}
p.future
}
}
object Main {
import scala.concurrent.ExecutionContext.Implicits.global
def main(args: Array[String]): Unit = {
val f = Future {
Thread.sleep(5);
1
}
val g = Future {
Thread.sleep(2000);
2
}
val result: Future[(Int, Int)] = for {
v1 <- new TimelyFuture(f, "f", 10 milliseconds)
v2 <- new TimelyFuture(g, "g", 10 milliseconds)
} yield (v1, v2)
val sum = Await.result(result, 1 seconds) // as expected, this throws exception : "RuntimeException: future g has timed out after 10 milliseconds"
}
}
If you are merely looking for informational metrics on which individual future was taking a long time (or in combination with others), your best bet is to use a wrapper when creating the futures to log the metrics:
object InstrumentedFuture {
def now() = System.currentTimeMillis()
def apply[T](name: String)(code: => T): Future[T] = {
val start = now()
val f = Future {
code
}
f.onComplete {
case _ => println(s"Future ${name} took ${now() - start} ms")
}
f
}
}
val future1 = InstrumentedFuture("Calculator") { /*...code...*/ }
val future2 = InstrumentedFuture("Differentiator") { /*...code...*/ }
You can check if a future has completed by calling its isComplete method
if (futureInt1.isComplete) { /*futureInt2 must be the culprit */ }
if (futureInt2.isComplete) { /*futureInt1 must be the culprit */ }
As a first approach i would suggest to lift your Future[Int] into Future[Try[Int]]. Something like that:
object impl {
def checkException[T](in: Future[T]): Future[Try[T]] =
in.map(Success(_)).recover {
case e: Throwable => {
Failure(new Exception("Error in future: " + in))
}
}
implicit class FutureCheck(s: Future[Int]) {
def check = checkException(s)
}
}
Then a small function to combine the results, something like that:
object test {
import impl._
val futureInt1 = Future{ 1 }
val futureInt2 = Future{ 2 }
def combine(a: Try[Int], b: Try[Int])(f: (Int, Int) => (Int)) : Try[Int] = {
if(a.isSuccess && b.isSuccess) {
Success(f(a.get, b.get))
}
else
Failure(new Exception("Error adding results"))
}
val futureSum = for {
int1 <- futureInt1.check
int2 <- futureInt2.check
} yield combine(int1, int2)(_ + _)
}
In futureSum you will have a Try[Int] with the integer or a Failure with the exception corresponding with the possible error.
Maybe this can be useful
val futureInt1 = getIntAsync1();
val futureInt2 = getIntAsync2();
val futureSum = for {
int1 <- futureInt1
int2 <- futureInt2
} yield (int1 + int2)
Try(Await.result(futureSum, 60 seconds)) match {
case Success(sum) => println(sum)
case Failure(e) => println("we got timeout. the unfinished futures are: " + List(futureInt1, futureInt2).filter(!_.isCompleted)
}
Scala futures:
With Await.result I can wait for the future to complete before terminating the program.
How can I wait for a Future.onFailure to complete before terminating the program?
package test
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
object Test5 extends App {
def step(i: Int): Future[String] = Future {
Thread.sleep(1000)
if (i == 3) throw new Exception("exception on t3")
Thread.sleep(1000); val s = s"done: $i"; println(s); s
}
val task: Future[String] = step(1).flatMap(_ => step(2).flatMap(_ => step(3)))
task.onFailure {
case e: Throwable => println("task.onFailure. START: " + e); Thread.sleep(3000); println("task.onFailure. END.")
}
try {
val result: String = Await.result(task, Duration.Inf)
println("RESULT: " + result)
} catch {
case e: Throwable => println("Await.result.try catch: " + e)
}
// without this, "task.onFailure. END." does not get printed.
// How can I replace this line with something such as Await.result([the task.onFailure I set previously])
Thread.sleep(5000)
println("END")
}
Note: Instead of using task.onFailure, I could catch the exception on Await.result (as in the example). But I would prefer to use task.onFailure.
Update
A solution is to use transform or recover as proposed by Ryan. In my case, I needed something more, and I've added this alternative answer: on scala, how to wait for a Future.onFailure to complete?
If all you want to do is to sequence a side-effect such that you know that it has been executed I'd recommend using 'andThen'.
onFailure returns Unit. There's no way to block on that.
Either catch the exception on Await.result or use transform or recover to side-effect on the exception.
You can't. Future#onFailure returns Unit so there is no handle for which you can grab to block execution.
Since you're blocking on the completion of task anyway, the catch clause in your code is equivalent to what you would get from onFailure. That is, if the Future fails, the body of catch will be executed before the program exits because it is running on the main thread (whereas onComplete is not).
We can enrich Future with onComplete2 and onCompleteWith2 as follows:
import scala.concurrent.{ExecutionContext, Future, Promise}
import scala.util.Try
object FutureUtils {
implicit class RichFuture[T](val self: Future[T]) {
def onComplete2[U](f: (Try[T]) => U)(implicit executor: ExecutionContext): Future[T] = {
val p = Promise[T]()
self.onComplete { r: Try[T] => f(r); p.complete(r) }
p.future
}
def onCompleteWith2[U](f: (Try[T]) => Future[U])(implicit executor: ExecutionContext): Future[T] = {
val p = Promise[T]()
self.onComplete { r: Try[T] => f(r).onComplete(_ => p.complete(r)) }
p.future
}
}
}
then I case use it as follows:
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
import FutureUtils._
import scala.util.{Failure, Success, Try}
object Test5 extends App {
def stepf(i: Int): Future[String] = Future { step(i) }
def step(i: Int): String = {
Thread.sleep(1000)
if (i == 3) throw new Exception("exception on t3")
Thread.sleep(1000); val s = s"done: $i"; println(s); s
}
val task1: Future[String] =
stepf(1).flatMap(_ => stepf(2).flatMap(_ => stepf(3)))
// the result of step(10) and step(11) is ignored
val task2: Future[String] = task1.onComplete2((r: Try[String]) => r match {
case Success(s) => step(10)
case Failure(e) => step(11)
})
/*
// If I want to recover (and so, to avoid an exception on Await.result:
val task3 = task2.recover {case e: Throwable => step(12) }
// I can use onCompleteWith2 instead of onComplete2
val task2: Future[String] = task1.onCompleteWith2((r: Try[String]) => r match {
case Success(s) => stepf(10)
case Failure(e) => stepf(11)
})
*/
try {
val result = Await.result(task2, Duration.Inf)
println("RESULT: " + result)
} catch {
// see my comment above to remove this
case e: Throwable => println("Await.result.try catch: " + e)
}
println("END.")
}
The execution is as follows:
done: 1
done: 2
done: 11
Await.result.try catch: java.lang.Exception: exception on t3
END.
If we don't throw the exception at step(3), the execution is as follows. Note that RESULT is "done: 3", not "done: 10":
done: 1
done: 2
done: 3
done: 10
RESULT: done: 3
END.
Here is the problem, I have a library which has a blocking method return Try[T]. But since it's a blocking one, I would like to make it non-blocking using Future[T]. In the future block, I also would like to compute something that's depend on the origin blocking method's return value.
But if I use something like below, then my nonBlocking will return Future[Try[T]] which is less convince since Future[T] could represent Failure[U] already, I would rather prefer propagate the exception to Future[T] is self.
def blockMethod(x: Int): Try[Int] = Try {
// Some long operation to get an Int from network or IO
throw new Exception("Network Exception") }
}
def nonBlocking(x: Int): Future[Try[Int]] = future {
blockMethod(x).map(_ * 2)
}
Here is what I tried, I just use .get method in future {} block, but I'm not sure if this is the best way to do that.
def blockMethod(x: Int): Try[Int] = Try {
// Some long operation to get an Int from network or IO
throw new Exception("Network Exception") }
}
def nonBlocking(x: Int): Future[Int] = future {
blockMethod(x).get * 2
}
Is this correct way to do that? Or there is a more scala idiomatic way to convert t Try[T] to Future[T]?
Here's an example that doesn't block, note that you probably want to use your own execution context and not scala's global context:
import scala.util._
import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
object Main extends App {
def blockMethod(x: Int): Try[Int] = Try {
// Some long operation to get an Int from network or IO
Thread.sleep(10000)
100
}
def tryToFuture[A](t: => Try[A]): Future[A] = {
future {
t
}.flatMap {
case Success(s) => Future.successful(s)
case Failure(fail) => Future.failed(fail)
}
}
// Initiate long operation
val f = tryToFuture(blockMethod(1))
println("Waiting... 10 seconds to complete")
// Should return before 20 seconds...
val res = Await.result(f, 20 seconds)
println(res) // prints 100
}
In my opinion: Try & Future is a monadic construction and idiomatic way to is monadic composition (for-comprehension):
That you need to define monad transformer for Future[Try[_]] (code for your library):
case class FutureT[R](run : Future[Try[R]])(implicit e: ExecutionContext) {
def map[B](f : R => B): FutureT[B] = FutureT(run map { _ map f })
def flatMap[B](f : R => FutureT[B]): FutureT[B] = {
val p = Promise[Try[B]]()
run onComplete {
case Failure(e) => p failure e
case Success(Failure(e)) => p failure e
case Success(Success(v)) => f(v).run onComplete {
case Failure(e) => p failure e
case Success(s) => p success s
}
}
FutureT(p.future)
}
}
object FutureT {
def futureTry[R](run : => Try[R])(implicit e: ExecutionContext) =
new FutureT(future { run })
implicit def toFutureT[R](run : Future[Try[R]]) = FutureT(run)
implicit def fromFutureT[R](futureT : FutureT[R]) = futureT.run
}
and usage example:
def blockMethod(x: Int): Try[Int] = Try {
Thread.sleep(5000)
if(x < 10) throw new IllegalArgumentException
else x + 1
}
import FutureT._
// idiomatic way :)
val async = for {
x <- futureTry { blockMethod(15) }
y <- futureTry { blockMethod(25) }
} yield (x + y) * 2 // possible due to using modan transformer
println("Waiting... 10 seconds to complete")
val res = Await.result(async, 20 seconds)
println(res)
// example with Exception
val asyncWithError = for {
x <- futureTry { blockMethod(5) }
y <- futureTry { blockMethod(25) }
} yield (x + y) * 2 // possible due to using modan transformer
// Can't use Await because will get exception
// when extract value from FutureT(Failure(java.lang.IllegalArgumentException))
// no difference between Failure produced by Future or Try
asyncWithError onComplete {
case Failure(e) => println(s"Got exception: $e.msg")
case Success(res) => println(res)
}
// Output:
// Got exception: java.lang.IllegalArgumentException.msg