是否有更好的monadic抽象替代方案来表示长时间运行的异步任务?

时间:2015-10-13 08:50:36

标签: scala asynchronous monads

Future擅长表示将在某个固定时间内完成的单个异步任务。

然而,存在另一种异步任务,其中不可能/很难确切地知道它何时完成。例如,特定字符串处理任务所花费的时间可能取决于各种因素,例如输入大小。

对于这类任务,通过检查任务是否能够在合理的时间内取得进展而不是通过设置Future中的硬超时值来检测故障可能会更好。

Scala中是否有任何库为这类任务提供合适的monadic抽象?

3 个答案:

答案 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 streamsscalaz 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]