I have two methods, let's call them load()
and init()
. Each one starts a computation in its own thread and returns a Future
on its own execution context. The two computations are independent.
val loadContext = ExecutionContext.fromExecutor(...)
def load(): Future[Unit] = {
Future
}
val initContext = ExecutionContext.fromExecutor(...)
def init(): Future[Unit] = {
Future { ... }(initContext)
}
I want to call both of these from some third thread -- say it's from main()
-- and perform some other computation when both are finished.
def onBothComplete(): Unit = ...
Now:
- I don't care which completes first
- I don't care what thread the other computation is performed on, except:
- I don't want to block either thread waiting for the other;
- I don't want to block the third (calling) thread; and
- I don't want to have to start a fourth thread just to set the flag.
If I use for-comprehensions, I get something like:
val loading = load()
val initialization = initialize()
for {
loaded <- loading
initialized <- initialization
} yield { onBothComplete() }
and I get Cannot find an implicit ExecutionContext.
I take this to mean Scala wants a fourth thread to wait for the completion of both futures and set the flag, either an explicit new ExecutionContext
or ExecutionContext.Implicits.global
. So it would appear that for-comprehensions are out.
I thought I might be able to nest callbacks:
initialization.onComplete {
case Success(_) =>
loading.onComplete {
case Success(_) => onBothComplete()
case Failure(t) => log.error("Unable to load", t)
}
case Failure(t) => log.error("Unable to initialize", t)
}
Unfortunately onComplete
also takes an implicit ExecutionContext
, and I get the same error. (Also this is ugly, and loses the error message from loading
if initialization
fails.)
Is there any way to compose Scala Futures without blocking and without introducing another ExecutionContext
? If not, I might have to just throw them over for Java 8 CompletableFutures or Javaslang Vavr Futures, both of which have the ability to run callbacks on the thread that did the original work.
Updated to clarify that blocking either thread waiting for the other is also not acceptable.
Updated again to be less specific about the post-completion computation.
Future.firstCompletedOf(List(initialization, loading))
if you want to grab the result of the Future that completes first. The for-list-comprehension will wait for both futures to complete before performing the yield. – pctingpromise.completeWith(initialization zip loading)
– Viktor Klang