我正在寻找一种方法,Task
(即外部作用域)可以使用flatMap
或类似方法执行“ subTask”,并确保外部作用域中的所有后续链接调用使用原始的调度程序。
使用的库和scala:
"io.monix" %% "monix" % "3.0.0-RC1"
"org.typelevel" %% "cats-core" % "1.0.1"
示例代码:
import monix.eval.Task
import monix.execution.Scheduler
import scala.concurrent.Await
import scala.concurrent.duration.Duration
import monix.execution.Scheduler.Implicits.global
import cats.implicits._
object Test extends App {
val io1 = Scheduler.io("io1")
val io2 = Scheduler.io("io2")
def taskEval(name: String) = Task.eval(println(s"Running eval Task [$name] on thread [${Thread.currentThread().getName}]"))
def subTask: Task[Unit] = {
taskEval("subTaskScope").executeOn(io2)
}
def outerScope(sub: Task[Unit]): Task[Unit] = {
taskEval("outerScopeBefore") *> sub *> taskEval("outerScopeAfter")
}
def outerScopeTryProtect(sub: Task[Unit]): Task[Unit] = {
taskEval("outerScopeBefore") *> (sub <* Task.shift) *> taskEval("outerScopeAfter")
}
val program1 = taskEval("programBefore").executeOn(io1) *> outerScope(subTask) *> taskEval("programAfter")
val program2 = taskEval("programBefore").executeOn(io1) *> outerScopeTryProtect(subTask) *> taskEval("programAfter")
Await.result(program1.runAsync, Duration.Inf)
// Running eval Task [programBefore] on thread [io1-573]
// Running eval Task [outerScopeBefore] on thread [io1-573]
// Running eval Task [subTaskScope] on thread [io2-574]
// Running eval Task [outerScopeAfter] on thread [io2-574] // << we don't shift back so we are stuck with the scheduler that is forces by subTask
// Running eval Task [programAfter] on thread [io2-574]
println("------")
// Running eval Task [programBefore] on thread [io1-573]
// Running eval Task [outerScopeBefore] on thread [io1-573]
// Running eval Task [subTaskScope] on thread [io2-574]
// Running eval Task [outerScopeAfter] on thread [scala-execution-context-global-575] // we shift the scheduler but this restores the default scheduler
// Running eval Task [programAfter] on thread [scala-execution-context-global-575]
Await.result(program2.runAsync, Duration.Inf)
}
subTask
方法希望在专用调度程序(io2
)上执行一些异步工作,因此它使用executeOn
强制调度程序异步边界。
outerScope
方法正在某个程序program1
中执行,它使用sub
调用subTask
(即flatMap
)。由于它没有任何明确的异步边界,因此如果subTask
恰巧更改了调度程序(确实如此),则其余outerScope
会使用subTask
更改的调度程序。因此,在taskEval("outerScopeAfter")
调度程序上执行对io2
的调用。
outerScopeTryProtect
通过在Task.shift
踏入flatMap
(即sub
)之后引入异步边界(使用subTask
)来尝试保护其使用的调度程序。但是,异步边界(Task.shift
)将调度程序重置为默认调度程序,在这种情况下,它将一路退回到program2.runAsync
中隐式使用的调度程序。这不是我们想要的,因为我们想回到调用taskEval("outerScopeBefore")
时使用的调度程序,即调度程序io1
。
我正在寻找的是类似Task[A].flatMap[B](f: A => Task[B]): Task[B]
的东西,它将以f
指定的任何方式执行f
产生的任务(可能使用其他调度程序),但是结果Task
调用中的flatMap
将返回到Task[A]
之前由flatMap
使用的调度程序。