Reputation: 2582
I'm converting Future
code to IO
. I have code similar to this
def doSomething: Future[Foo] = {
Future {
//some code the result of which we don't care about
}
Future {
//Foo
}
}
And then at the end of the program, I doSomething.unsafeRunSync
. How do I convert these Future
s to IO
s while maintaining the fire-and-forget functionality of the first Future
? In using IO
's async API, I am worried about accidentally blocking the thread when I later call unsafeRunSync
on doSomething
.
Upvotes: 6
Views: 2455
Reputation: 8227
I believe that you need to wrap the first Future
in such a way that it completes immediately. We ignore exeptions, or catch them however, but they are contained within its own thread. The parameter cb
is the promise that needs to complete; so we short-circuit the completion by providing a value immediately.
def firstFuture(implicit ec: ExecutionContext): IO[Unit] = {
IO.async[Unit] { cb =>
ec.execute(() => {
try {
//some code the result of which we don't care about
} catch {
}
})
cb(Right(()))
}
}
In the for-comprehension, the firstFuture
will complete immediately even though its thread will have a long-running task active on it.
def doSomething(implicit ec: ExecutionContext): IO[Foo] = {
for {
_ <- firstFuture
IO.async[Foo] { fb =>
// Foo
}
}
}
Upvotes: 2
Reputation: 3988
A solution that uses only cats-effect
could use IO.start
. This, combined with the fact that you will then never join the resulting Fiber
, will look something like this:
import cats.effect._
import cats.implicits._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
object ExampleApp extends App{
val fireAndForget =
IO(println("Side effect pre-sleep")) *>
IO.sleep(2.seconds) *>
IO(println("Side effect post-sleep"))
val toBeUsed = IO{
println("Inside second one")
42
}
val result = for {
fiber <- IO.shift *> fireAndForget.start
res <- toBeUsed.handleErrorWith { error =>
// This is just in case you 'toBeUsed' can actually fail,
// and you might want to cancel the original side-effecting IO
fiber.cancel *> IO.raiseError(error) }
} yield res
println(result.unsafeRunSync())
println("Waiting 3 seconds...")
IO.sleep(3.seconds).unsafeRunSync()
println("Done")
}
This will print (most of the times) something similar to:
Side effect pre-sleep
Inside second one
42 // Up until here, will be printed right away
Waiting 3 seconds... // It will then be waiting a while
Side effect post-sleep // ...at which point the side effecting code terminates
Done
Finally, here are more details about Fiber and IO.shift
Upvotes: 10