• Java Future modeled async operations • Minimal ways to observe and react • Twitter and Akka • Scala SIP-14, Jan 2012 • Scala standard Future in 2.10 6
a Future? SCALA FUTURES • 3 states • Unresolved • Resolved with Success and a value • Resolved with Failure and a Throwable • Immediately resolved instances • Promises • Rich set of combinators in class and companion object
with apply() SCALA FUTURES 11 import scala.concurrent.ExecutionContext.Implicits.global val myFuture: Future[Int] = Future { calculateSlowly() } Creating a Future with Promise val myPromise = Promise[Int]() val myNewFuture = myPromise.future val res = calculateSlowly() myPromise.success(res)
an ExecutionContext SCALA FUTURES • Transform body is submitted to EC as a Callable • Global execution context – ForkJoin pool • Build from any Executor or ExecutionService • Why might you chose one over another? • Control over pool size and characteristics • ForkJoinPool is not ideal for long blocking operations • Certain work in a particular pool 17
model is different from Scala TWITTER FUTURES • Transform body is executed in the prior thread • Pipeline executed by same thread • Work can be forked to a new pool • Will these necessarily be in order? Depth first or something else? • If already resolved, new callback executes immediately in caller thread • Future.apply() runs body immediately in caller thread • New Future creation needs explicit FuturePool 24
futures TWITTER FUTURES val pool = FuturePool.unboundedPool val notf = Future { // executes immediately in caller thread calculate() } val f = pool { // executes in the pool calculate() } 25
cancellation TWITTER FUTURES • Promise raise() – marks promise as interrupted • Future is NOT resolved • Promise isInterrupted() • Running calculation is not stopped • Calculation can check isInterruped() and interrupt itself • raiseWithin() – NEW future which fails after the timeout 26
val pool = FuturePool.unboundedPool val promise = new Promise[Int]() pool { val r1 = expensiveCalculation() promise.isInterrupted match { case Some(t) => promise.setException(t) case None => promise.setValue(nextCalculation(r1)) } } promise.raise(new IllegalStateException(“I give up”)) 27
LISTENABLEFUTURE • Google Guava core library • Brought functional collections to Java • ListenableFuture provides Futures with callbacks • From ca 2011 • Before Scala Future • Before CompletableFuture
from Scala GUAVA LISTENABLEFUTURE • Not chainable • Requires type hint • Relies on GuavaFunction and AsyncFunction • transform() vs transformAsync() (in Guava 20.0) • ExecutionContext required (in Guava 21.0) 34
COMPLETABLEFUTURE • Java8 • Future with callbacks • Acts as both Promise and Future • Creation • As Promise • In an executor via supplyAsync(Supplier) • In an executor via runAsync(Runnable)
COMPLETABLEFUTURE val cf = new CompletableFuture[Int]() val x1: CompletableFuture[Int] = cf.thenApplyAsync( (i: Int) => scaleTransform(i)) // final stage – deal with the result x1.handle((r, t) => if (r != null) doHappyPath(r) else if (t != null) doExceptionPath(t)) // some time later cf.complete(41) 38
COMPLETABLEFUTURE • Different methods to do direct or async operations • apply(), applyAsync() • handle() handleAsync() • Regular version uses the same thread • Async versions use default executor or specified executor • Default executor is ForkJoinPool.commonPool() • Pool sized based on number of cores • New thread if commonPool is not configured for parallelism 39
and combinators compare? JAVA COMPLETABLEFUTURE • CompletableFuture operations provide variants: • Runnable / Supplier • Function / Consumer • No recoverWith, just recover (exceptionally) • Cannot replace a failure with an unresolved fallback • Combinators execute next step without intermediate Future • Scala zip() resolves with a tuple, map() to execute next step • foo.thenCombine(bar, (a,b) => doSomethingWith(a,b)) • Overall handler method taking nullable success and failure • handle(BiFunction<? super T,Throwable,? extends U> fn) 40
thought of as lazy Futures • Scalaz, Monix, FS2 • Explicitly run: attemptRun(), runAsync(), unsafeRunAsync() • No unwanted computations • No leaked computations • Memoization • Run every time by default • Memoize the result on demand • Trampolined • Cancelable
Java Promise-like creation Promise Promise SettableFuture CompletableFuture Async creation Future.apply() myPool.apply() ListeningDecorator execSvc.submit() supplyAsync() Execution model Run in ExecutionContext Completing thread runs Completing thread or executor (method overload) Completing thread or executor (different method) Supports for- comprehension Yes Yes No (yes with gfc-guava) No (possible with conversions) Cancellation No Yes (cooperative) No Yes (no interrupt despite argument) Java/Scala interoperation Implement e.g. PartialFunction from Java Variants provided for interop Java fair, Scala bad Better with gfc-guava Java good, type inference issues Future comparison CONCLUSIONS 45
recommend? CONCLUSIONS • Use whatever your libraries use • If you aren’t introducing Twitter, stick with Scala unless you need cancellation or monitoring combinators • If you need to mix Scala and Twitter, use Bijection • If you need to interop with Java, prefer CompletableFuture • At this point, avoid Guava ListenableFuture 46