Поведение «жадно оценивать и забывать» для Cats Effect IO - PullRequest
0 голосов
/ 15 мая 2018

Я конвертирую Future код в IO.У меня есть код, подобный этому

def doSomething: Future[Foo] = {
  Future { 
    //some code the result of which we don't care about 
  }
  Future {
    //Foo
  }
}

И затем в конце программы, я doSomething.unsafeRunSync.Как мне преобразовать эти Future s в IO s, сохраняя при этом функциональность первого запуска Future?При использовании асинхронного API IO меня беспокоит случайная блокировка потока при последующем вызове unsafeRunSync на doSomething.

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

Решение, которое использует только cats-effect, может использовать IO.start. Это, в сочетании с тем, что вы никогда не присоединитесь к полученному Fiber, будет выглядеть примерно так:

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")
}

Это напечатает (в большинстве случаев) что-то похожее на:

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 

Наконец, вот более подробная информация о Fiber и IO.shift

0 голосов
/ 15 мая 2018

Я считаю, что вам нужно обернуть первый Future таким образом, чтобы он завершился немедленно. Мы игнорируем исключения или ловим их, но они содержатся в его собственном потоке. Параметр cb - это обещание, которое необходимо выполнить; поэтому мы закорачиваем завершение, предоставляя значение немедленно.

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(()))
  }
}

В режиме «для понимания» firstFuture завершится немедленно, даже если в его потоке будет активна долгосрочная задача.

def doSomething(implicit ec: ExecutionContext): IO[Foo] = {
  for {
    _ <- firstFuture
    IO.async[Foo] { fb =>
      // Foo
    }
  }
}
...