据我记得在标准库中没有Future timeout 的实用程序。
您将如何中断/取消 JVM 上正在进行的计算?在一般情况下,您不能,您只能在wait 上中断Thread,但如果它从不waits?用于异步计算(定义取消)的 IO 库将 IO 执行为一系列较小的不可中断任务(每个 map/flatMap 创建一个新步骤),如果它们收到取消/超时,那么它们将继续执行当前任务(因为它们无法停止它) 但他们不会开始下一个。您可以在超时时返回异常,但仍然会执行最后一步,因此如果它是一些副作用(例如数据库操作),它将在您返回失败后完成。
这是不直观且棘手的,我认为这就是为什么没有将这种行为添加到标准库中的原因。
此外,未来正在进行中,可能会产生副作用。您不能获取 Future[A] 类型的值并重新运行它。但是,您可以将 future 作为 by-name 参数传递,以便在 .recoverWith 中您可以重新创建 future。
很遗憾,您可以实现类似“重试直到 LocalDateTime.now - startTime >= ”,因为我认为这是您想要的:
def retry[A](future: => Future[A], attemptsLeft: Int, timeoutTime: Instant) =
future.recoverWith {
case error: Throwable =>
if (attemptsLeft <= 0 || Instant.now.isAfter(timeoutTime)) Future.failure(error)
else retryHelper(future, attemptsLeft - 1, timeoutTime)
}
这可以与Future.sequence 结合以创建结果列表:
def retryFutures[A](list: List[() => Future[A]) = {
val attempts: Int = ...
val timeout: Instant = ...
Future.sequence(list.map(future => retry(future(), attempts, timeout)))
}
如果您想跟踪哪个未来失败和哪个成功:
def futureAttempt[A](future: Future[A]): Future[Either[Throwable, A]] =
future.map(a => Right(a))).recover {
case error: Throwable => Left(error)
}
def retryFutures[A](list: List[() => Future[A]) = {
val attempts: Int = ...
val timeout: Instant = ...
Future.sequence(list.map(future => retry(futureAttempt(future()), attempts, timeout)))
}
如果您不担心取消 JVM 上的期货,并且如果您有更多类似的情况,我建议您使用库。
如果您想使用为您实现重试的东西,请使用cats-retry
如果您想在定义计算时获得比 Future 更好的东西(例如,不需要您使用别名参数或空函数的东西)试试 Monix 或 ZIO (https://zio.dev/)