I have to admit that I like Scala’s design of futures much more than I like Java’s, and one of big differentiators that I like is exactly that Scala futures aren’t cancellable by any client code receiving a Future
object.
Scala very clearly separates the concerns into a responsibility for completing a future (with success or failure, one of which can admittedly be a timeout exception): that’d be Promise
. On the other hand, you can safely hand out a Future
object to callers because they can’t affect its completion. A future in Scala is simply a monad (yes, I just said that) for a value that might be available now or in the future (including the case of not being available ever.)
If you want a timeout on a future, it’s relatively easy to code that up by making a promise, trying to complete it with that future, and separately scheduling a timeout that when it fires, it also tries to complete the promise by failing it with TimeoutException failure. Finally, return this promise’s future in place of the original future.
Of course, it won’t magically cancel the execution of whatever the task is (or tasks are!) that are working to fulfill the future (even in my above timeout-with-promises scenario now you have at least two tasks: the one working to fulfill the original future, and the timeout task).
Cancellation is a completely different concern and if it’s necessary it should be something exposed as a separate API by the original producer of the future somehow independently of the future object itself, I think.
The main issue here is that not every Future has a single thread somewhere toiling to produce its result that you can easily cancel. Some Future objects represents the end result achieved through transformations, chaining, and joins (e.g. Future.sequence
, Future.foldLeft
and friends) of multiple other asynchronous computations. What would “cancelling” the ultimate result future of all these async computations mean? It’d be a pretty big support burden to provide correct and intuitive cancellation semantics as well as ways to implement those semantics.
I think that in the long term, the current Scala design spares a lot of headache by not requiring the burden of a correctly implemented cancellation. I recognize there might be valid reasons to cancel execution of an async scheduled computation, I’m just saying that such functionality should probably exist somewhat orthogonal to the Scala Futures and might often be a bespoke implementation depending on what the computation itself looks like.
At the end of the day, async computation tasks are separate from objects representing their outcomes.