So I have a api in Scala that uses twitter.util.Future
.
In my case, I want to create 2 futures, one of which is dependent on the result of the other and return the first future:
def apiFunc(): Future[Response]={
val future1 = getFuture1()
val future2 = future1 map {
resp => processFuture1Resp(resp)
}
future1
}
So in this case, future2
is never consumed and the api returns the result of future1
before future2
is completed.
My question is - will future2
run even though the api has returned?
Further, future1
should not be affected by future2
. That is, the processing time for future2
should not be seen by any client that calls apiFunc()
. You can think of processFuture1Resp
as sending the results of getFuture1()
to another service but the client calling apiFunc()
doesn't care about that portion and only wants future1
as quickly as possible
My understanding is that futures spawn threads, but I am unsure if that thread will be terminated after the return of the main thread.
I guess a different way to ask this is - Will a twitter.util.future
always be executed? Is there a way to fire and forget a twitter.util.future
?
CodePudding user response:
If you want to chain two futures where one of them processes the result of the other (and return a future), you can use a for comprehension:
def getFuture1() = {
println("Getting future 1...")
Thread.sleep(1000)
Future.successful("42")
}
def processFuture1Resp(resp: String) = {
println(s"Result of future 1 is: ${resp}")
"END"
}
def apiFunc(): Future[String]={
for {
res1 <- getFuture1()
res2 <- Future(processFuture1Resp(res1))
} yield res2
}
def main(args: Array[String]) {
val finalResultOfFutures: Future[String] = apiFunc()
finalResultOfFutures
Thread.sleep(500)
}
This will print:
Getting future 1...
Result of future 1 is: 42
The value finalResultOfFutures
will contain the result of chaining both futures, and you'll be sure that the first future is executed before the second one. If you don't wait for the execution of finalResultOfFutures
on the main thread (commenting the last sleep function of the main thread), you will only see:
Getting future 1...
The main thread will finish before the second future has time to print anything.
Another (better) way to wait for the execution of the future would be something like this:
val maxWaitTime: FiniteDuration = Duration(5, TimeUnit.SECONDS)
Await.result(finalResultOfFutures, maxWaitTime)
Await.result
will block the main thread and waits a defined duration for the result of the given Future. If it is not ready or completes with a failure, Await.result will throw an exception.
EDITED
Another option is to use Monix Tasks This library allows you wrap actions (such as Futures) and have a greater control on how and when the Futures are executed. Since a Future may start its execution right after its declaration, these functionalities can be quite handy. Example:
import monix.execution.Scheduler.Implicits.global
import scala.concurrent.duration._
def getFuture1() = {
println("Getting future 1...")
Thread.sleep(3000)
println("Future 1 finished")
Future.successful("42")
}
def processFuture1Resp(resp: Task[String]) = {
val resp1 = resp.runSyncUnsafe(2.seconds)
println(s"Future2: Result of future 1 is: ${resp1}")
}
def main(args: Array[String]) {
// Get a Task from future 1. A Task does not start its execution until "run" is called
val future1: Task[String] = Task.fromFuture(getFuture1())
// Here we can create the Future 2 that depends on Future 1 and execute it async. It will finish even though the main method ends.
val future2 = Task(processFuture1Resp(future1)).runAsyncAndForget
// Now the future 1 starts to be calculated. Yo can runAsyncAndForget here or return just the task so that the client
// can execute it later on
future1.runAsyncAndForget
}
This will print:
Getting future 1...
Future 1 finished
Future2: Result of future 1 is: 42
Process finished with exit code 0