猫效应 IO 的急切评估和忘记行为
Eagerly-evaluate-and-forget behavior for Cats Effect IO
我正在将 Future
代码转换为 IO
。我有类似的代码
def doSomething: Future[Foo] = {
Future {
//some code the result of which we don't care about
}
Future {
//Foo
}
}
然后在节目结束时,我doSomething.unsafeRunSync
。如何将这些 Future
转换为 IO
,同时保持第一个 Future
的即发即弃功能?在使用 IO
的异步 API 时,我担心稍后在 doSomething
上调用 unsafeRunSync
时会意外阻塞线程。
我认为您需要以立即完成的方式包装第一个 Future
。我们忽略异常,或者捕获它们,但它们包含在它自己的线程中。参数cb
是需要完成的promise;所以我们通过立即提供一个值来短路完成。
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(()))
}
}
在 for-comprehension 中,firstFuture
将立即完成,即使它的线程将有一个长时间的 运行 任务在其上处于活动状态。
def doSomething(implicit ec: ExecutionContext): IO[Foo] = {
for {
_ <- firstFuture
IO.async[Foo] { fb =>
// Foo
}
}
}
仅使用 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
的细节
我正在将 Future
代码转换为 IO
。我有类似的代码
def doSomething: Future[Foo] = {
Future {
//some code the result of which we don't care about
}
Future {
//Foo
}
}
然后在节目结束时,我doSomething.unsafeRunSync
。如何将这些 Future
转换为 IO
,同时保持第一个 Future
的即发即弃功能?在使用 IO
的异步 API 时,我担心稍后在 doSomething
上调用 unsafeRunSync
时会意外阻塞线程。
我认为您需要以立即完成的方式包装第一个 Future
。我们忽略异常,或者捕获它们,但它们包含在它自己的线程中。参数cb
是需要完成的promise;所以我们通过立即提供一个值来短路完成。
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(()))
}
}
在 for-comprehension 中,firstFuture
将立即完成,即使它的线程将有一个长时间的 运行 任务在其上处于活动状态。
def doSomething(implicit ec: ExecutionContext): IO[Foo] = {
for {
_ <- firstFuture
IO.async[Foo] { fb =>
// Foo
}
}
}
仅使用 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
的细节