Search code examples
scalascala-cats

Eagerly-evaluate-and-forget behavior for Cats Effect IO


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 Futures to IOs 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.


Solution

  • 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