Как Задача вызывающей стороны избежать того, чтобы ее планировщик изменился подзадачей, над которой она перешла

Я ищу способ, которым Task (т. е. внешняя область) может выполнить "подзадачу", используя flatMap или что-то эквивалентное и убедитесь, что любые последующие вызовы в цепочке во внешней области используют оригинальный планировщик.

Используемые библиотеки и скалы:

  • Скала - 2.12.4
  • моникс - "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) после flatMapPED 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,

0 ответов

Другие вопросы по тегам