Future
擅长表示将在某个固定时间内完成的单个异步任务。
然而,存在另一种异步任务,其中不可能/很难确切地知道它何时完成。例如,特定字符串处理任务所花费的时间可能取决于各种因素,例如输入大小。
对于这类任务,通过检查任务是否能够在合理的时间内取得进展而不是通过设置Future
中的硬超时值来检测故障可能会更好。
Scala中是否有任何库为这类任务提供合适的monadic抽象?
答案 0 :(得分:2)
您可以使用这样的值流:
sealed trait Update[T]
case class Progress[T](value: Double) extends Update[T]
case class Finished[T](result: T) extends Update[T]
让你的任务在方便的时候发出Progress值(例如每次计算的一部分完成),并在完成整个计算后发出一个Finished值。消费者可以检查进度值以确保任务仍在进行中。如果消费者对进度更新不感兴趣,您可以将其过滤掉。我认为这比基于演员的方法更具有组合性。
根据您需要的性能或纯度,您可能需要查看akka streams或scalaz streams。 Akka流有一个纯DSL用于构建流程图,但允许在处理阶段的可变性。 Scalaz流更具功能性,但我听到的性能较低。
答案 1 :(得分:0)
你可以将你的工作分成几块。每个块都是Future
,超时 - 你的合理进度的概念。将这些期货连在一起以完成任务。
示例1 - 两个块可以并行运行并且不相互依赖(令人尴尬的并行任务):
val chunk1 = Future { ... } // chunk 1 starts execution here
val chunk2 = Future { ... } // chunk 2 starts execution here
val result = for {
c1 <- chunk1
c2 <- chunk2
} yield combine(c1, c2)
示例2 - 第二个块取决于第一个:
val chunk1 = Future { ... } // chunk 1 starts execution here
val result = for {
c1 <- chunk1
c2 <- Future { c1 => ... } // chunk 2 starts execution here
} yield combine(c1, c2)
当你有许多期货如sequence
时,显然还有其他一些建议可以帮助你。
答案 2 :(得分:0)
文章&#34; Scala代码中最糟糕的事情:Futures&#34; Ken Scambler指出需要分离关注点:
scala.concurrent.Future
可以自然地与scala.util.Try
一起使用,因此我们的代码经常以笨拙的方式使用Try
到处代表失败,使用原始异常作为失败值,即使没有异常被抛出也是如此- 要对
scala.concurrent.Future
执行任何操作,您需要使用隐式ExecutionContext
。这种令人讨厌的依赖需要在所使用的任何地方进行操作。
因此,如果您的代码不直接依赖于Future
,而是依赖于简单的Monad属性,则可以使用Monad
类型对其进行抽象:
trait Monad[F[_]] {
def flatMap[A,B](fa: F[A], f: A => F[B]): F[B]
def pure[A](a: A): F[A]
}
// read the type parameter as “for all F, constrained by Monad”.
final def load[F[_]: Monad](pageUrl: URL): F[Page]