1

假设我有一个 IO(因此不安全)操作将返回truefalse. 我想使用 Zio 调度机制来执行此操作,直到值为true,但最多只能执行 N 次。采用文档中的代码并将其更改为我想要实现的...

import zio._
import zio.duration._
import zio.console._
import zio.clock._
import java.util.Random

object API {
  // our API method will return true about 30% of the time, but
  // return false the rest of the time (instead of throwing an
  // exception, as is shown in documentation)
  def makeRequest: Task[Boolean] = Task.effect {
    new Random().nextInt(10) > 7
  }
}

object ScheduleUtil {
  def schedule[A] = Schedule.spaced(1.second) && Schedule.recurs(4).onDecision({
    case Decision.Done(_)                 => putStrLn(s"done trying")
    case Decision.Continue(attempt, _, _) => putStrLn(s"attempt #$attempt")
  })
}

import ScheduleUtil._
import API._

object ScheduleApp extends scala.App {

  implicit val rt: Runtime[Clock with Console] = Runtime.default

  rt.unsafeRun(makeRequest.retry(schedule).foldM(
    ex => putStrLn("Exception Failed"),
    v => putStrLn(s"Succeeded with $v"))
  )
}

// run the app
ScheduleApp.main(Array())

这当然行不通。输出是Succeeded with falseor (偶尔) Succeeded with true。我尝试添加Schedule.recurUntilEqualsSchedule定义中,但无济于事。

object ScheduleUtil {
    def schedule[A] = Schedule.spaced(1.second) && Schedule.recurUntilEquals(true) && Schedule.recurs(4).onDecision({
      case Decision.Done(_)                 => putStrLn(s"done trying")
      case Decision.Continue(attempt, _, _) => putStrLn(s"attempt #$attempt")
    })
  }

import ScheduleUtil._

// re-define ScheduleApp in the exact same way as above, and the following error results:

cmd93.sc:5: polymorphic expression cannot be instantiated to expected type;
 found   : [A]zio.Schedule[zio.console.Console,Boolean,((Long, Boolean), Long)]
    (which expands to)  [A]zio.Schedule[zio.Has[zio.console.Console.Service],Boolean,((Long, Boolean), Long)]
 required: zio.Schedule[?,Throwable,?]
  rt.unsafeRun(makeRequest.retry(schedule).foldM(

如何使用 Zio 调度程序完成这样的用例?当然,我可以重新定义makeRequest任务以故意抛出异常,而不是返回 false,这与文档中的工作方式一样。但我希望避免不必要的异常生成/处理。

object API {
    // our API method will return true about 30% of the time, but
    // return false the rest of the time (instead of throwing an
    // exception, as is shown in documentation)
    def makeRequest = Task.effect {
      if (new Random().nextInt(10) > 7) true else throw new Exception("Not true")
    }
  }
4

2 回答 2

2

您的问题是您使用retryeffect不是repeat您想要的,因为您将明确绕过您提到的错误通道。

所以只需更改makeRequest.retry(schedule)makeRequest.repeat(schedule)它应该可以工作。

有关更详细的描述,请考虑以下签名:

// Schedule.spaced
def spaced(duration: Duration): Schedule[Any, Any, Long]

// Schedule.recurs
def recurs(n: Int): Schedule[Any, Any, Long]

// Schedule.recurUntilEquals
def recurUntilEquals[A](a: => A): Schedule[Any, A, A]

Schedule具有三个类型参数,-Env-In+Out, 与属于 的标准类型Env相同,但与标准和其他类型不同。这是因为根据文档描述了“循环计划,它使用 type 的值,并返回 type 的值”。For和输入表明它将接受任何输入值,并且通过扩展也不会限制该值。您可以通过将两者组合在一起来看到这一点:RZIOInOutEAZIOScheduleInOutspacedrecursAny

val s: Schedule[Any, Any, (Long, Long)] = Schedule.spaced(1.second) && Schedule.recurs(1)

这也是为什么它在用作 的一部分时不会导致任何编译器错误的原因retry,因为它们在不使用错误通道时对错误通道没有任何特定要求。但这也隐藏了您的问题,因为仅在出现错误retry时才使用计划,但由于您返回或最终没有收到错误并且您的计划从未被调用。truefalse

添加后recurUntilEquals,输入约束将添加到计划中:

val s: Schedule[Any, Boolean, ((Long, Long), Boolean)] = Schedule.spaced(1.second) && Schedule.recurs(1) && Schedule.recurUntilEquals(true)

现在您说应该输入的输入Schedule实际上是一个布尔值,但retry具有签名:

def retry[R1 <: R, S](policy: Schedule[R1, E, S])(implicit ev: CanFail[E]): ZIO[R1 with Clock, E, A]

请注意,参数中的第二个位置Schedule是参数,它是错误类型,因为=!=你会得到一个编译器错误。policyEThrowableBoolean

相应地,这是签名repeat

def repeat[R1 <: R, B](schedule: Schedule[R1, A, B]): ZIO[R1 with Clock, E, B]

在这里,我们看到schedule实际采用的A类型在这种情况下是来自您的 API 的响应,或者Boolean与您在提供的时间表中所期望的匹配。

于 2020-11-21T10:30:45.067 回答
0

我正在使用 ZIO.repeatWhile(task)(condition) 这对我的情况非常有效。

于 2021-04-28T15:58:31.613 回答