Search code examples
scalascala-catscats-effectfs2

How to make cancellable timeout callback?


I want that user can run a timer with callback and able to cancel it. Something like this:

def main: F[Unit] =
  for
    cancel       <- runTimer(callback, 5.seconds)
    shouldCancel <- askUser
    _            <- cancel.whenA(shouldCancel)
  yield ()

How can I do it in terms of Cats of FS2?


Solution

  • The code that you need is actually pretty simple thanks to cats-effect being very powerful at solving concurrency problems:

    def runTimer(
      callback: IO[Unit],
      delay: Duration,
      cancel: IO[Unit]
    ): IO[Unit] =
      IO.race(
        IO.sleep(delay) >> callback.uncancelable,
        cancel
      ).void
    

    The key aspects are as follows:

    • Using IO.sleep to delay the execution of the callback.
    • Using uncancelable to make the callback uncancelable.
    • Using IO.race to run both the delayed callback and the cancel concurrently. If the cancel completes before the delay, the race will cancel the execution of the callback.

    You could improve this to ensure that cancel can't cancel the callback, but allow an upper cancellation to cancel the whole race.


    You can see a demo of the code running here: https://scastie.scala-lang.org/BalmungSan/UP6N6wMBTQGLHPjOmXS9mw/1