Java Future has cancel
method, which can interrupt the thread, which runs the Future
task. For example, if I wrap an interruptible blocking call in a Java Future
I can interrupt it later.
Scala Future provides no cancel
method. Suppose I wrap an interruptible blocking call in a Scala Future
. How can I interrupt it?
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.
A Future is a placeholder object for a value that may not yet exist. Generally, the value of the Future is supplied concurrently and can subsequently be used. Composing concurrent tasks in this way tends to result in faster, asynchronous, non-blocking parallel code.
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.
Promise. 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.
This is not yet a part of the Future
s API, but may be added as an extension in the future.
As a workaround, you could use the firstCompletedOf
to wrap 2 futures - the future you want to cancel and a future that comes from a custom Promise
. You could then cancel the thus created future by failing the promise:
def cancellable[T](f: Future[T])(customCode: => Unit): (() => Unit, Future[T]) = { val p = Promise[T] val first = Future firstCompletedOf Seq(p.future, f) val cancellation: () => Unit = { () => first onFailure { case e => customCode} p failure new Exception } (cancellation, first) }
Now you can call this on any future to obtain a "cancellable wrapper". Example use-case:
val f = callReturningAFuture() val (cancel, f1) = cancellable(f) { cancelTheCallReturningAFuture() } // somewhere else in code if (condition) cancel() else println(Await.result(f1))
EDIT:
For a detailed discussion on cancellation, see Chapter 4 in the Learning concurrent programming in Scala book.
I haven't tested this, but this expands on the answer of Pablo Francisco Pérez Hidalgo. Instead of blocking waiting for the java Future
, we use an intermediate Promise
instead.
import java.util.concurrent.{Callable, FutureTask} import scala.concurrent.{ExecutionContext, Promise} import scala.util.Try class Cancellable[T](executionContext: ExecutionContext, todo: => T) { private val promise = Promise[T]() def future = promise.future private val jf: FutureTask[T] = new FutureTask[T]( new Callable[T] { override def call(): T = todo } ) { override def done() = promise.complete(Try(get())) } def cancel(): Unit = jf.cancel(true) executionContext.execute(jf) } object Cancellable { def apply[T](todo: => T)(implicit executionContext: ExecutionContext): Cancellable[T] = new Cancellable[T](executionContext, todo) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With