I've got simple cats-effect app, which download site from the URL given as argument. During downloading app is supposed to display "loading bar" by writting dots (.
) to console. I implemented it by doing race of two IOs one for downloading another for displaying dots.
This is whole app on scastie.
The most important part is here:
def loader(): IO[Unit] = for {
_ <- console.putStr(".")
_ <- timer.sleep(Duration(50, MILLISECONDS)) *> loader()
} yield {}
def download(url: String): IO[String] = IO.delay(Source.fromURL(url)).map(_.mkString)
def run(args: List[String]): IO[Unit] = {
args.headOption match {
case Some(url) =>
for {
content <- IO.race(download(url), loader()).map(_.left.get)
_ <- console.putStrLn() *> console.putStrLn(s"Downloaded site from $url. Size of downloaded content is ${content.length}.")
} yield {}
case None => console.putStrLn("Pass url as argument.")
}
}
Everything works as I expected, when I run it, I get:
.............. Downloaded site from https://www.scala-lang.org. Size of downloaded content is 47738.
Only problem is that app never exits.
As far as I checked loader IO gets cancelled correctly. I can even add something like this:
urlLoader.run(args) *> console.putStrLn("???") *> IO(ExitCode.Success)
And ???
gets displayed.
Also when I remove race, then app exits correctly.
So my question how can I fix this and make my app exit at the end?