This is a different approach for computing the elapsed time, might need a bit more customization (like handling of failure, NonFatal
most probably), but I think this is a bit more idiomatic. It works only with Future
s though.
First create a class for the result with time (could be replaced with a tuple too) and one for the failure with time.
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
case class ResultWithTime[ResultType](val result: ResultType, val time: Long)
class FailureWithTime(failure: Throwable, val time: Long) extends Throwable(failure)
Then create a method that wraps the future to execute and handles the case when it fails (might use Promise
if you do not like the Future
's map
and recoverWith
methods (as they require an implicit ExecutionContext
)).
object TimerFuture {
def apply[ResultType](fut: =>Future[ResultType]): Future[ResultWithTime[ResultType]] = {
val startTime = System.currentTimeMillis
fut.map(r => ResultWithTime(r, System.currentTimeMillis - startTime))
.recoverWith{case t:Throwable =>
Future.failed(new FailureWithTime(t, System.currentTimeMillis - startTime))
}
}
}
Here is an example how to use it:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.io.StdIn
object Y extends App {
TimerFuture(Future{Thread.sleep(200); "Hello"})
.onSuccess({case ResultWithTime(r, t) => println(s"Took $t ms to get: $r")})
TimerFuture(Future{Thread.sleep(100); throw new NullPointerException()})
.onFailure({case f: FailureWithTime => println(s"Took ${f.time} ms to get: ${f.getCause}")})
StdIn.readLine()
}