如何在Scala中实现未来作为应用? [英] How to implement Future as Applicative in Scala?

查看:243
本文介绍了如何在Scala中实现未来作为应用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我需要运行两个并发计算,等待它们,然后合并他们的结果。更具体地说,我需要运行 f1:X1 => Y1 f2:X2 => Y2 ,然后调用 f:(Y1,Y2)=> Y

Suppose I need to run two concurrent computations, wait for both of them, and then combine their results. More specifically, I need to run f1: X1 => Y1 and f2: X2 => Y2 concurrently and then call f: (Y1, Y2) => Y to finally get a value of Y.

我可以创建 fut1:X1 =>未来[Y1] fut2:X2 =>未来[Y2] 然后组合他们以获得 fut:(X1,X2)=>未来[Y] 使用monadic组合。

I can create future computations fut1: X1 => Future[Y1] and fut2: X2 => Future[Y2] and then compose them to get fut: (X1, X2) => Future[Y] using monadic composition.

问题是monadic组成意味着顺序等待。在我们的情况下,这意味着我们先等待一个未来,然后我们将等待另一个。例如。如果需要2秒。到第一个未来完成,只有1秒。到第二个未来,我们浪费了1秒。

The problem is that monadic composition implies sequential wait. In our case it implies that we wait for one future first and then we will wait for another. For instance. if it takes 2 sec. to the first future to complete and just 1 sec. to the 2nd future to fail we waste 1 sec.

因此,看起来我们需要一个应用期货组合, > 完成或至少一个未来失败。是否有意义 ?

Thus it looks like we need an applicative composition of the futures to wait till either both complete or at least one future fails. Does it make sense ? How would you implement <*> for futures ?

推荐答案

如何实现< *>

None of the methods in other answers does the right thing in case of a future that fails quickly plus a future that succeeds after a long time.

但是这样的方法可以手动执行:

But such a method can be implemented manually:

def smartSequence[A](futures: Seq[Future[A]]): Future[Seq[A]] = {
  val counter = new AtomicInteger(futures.size)
  val result = Promise[Seq[A]]()

  def attemptComplete(t: Try[A]): Unit = {
    val remaining = counter.decrementAndGet
    t match {
      // If one future fails, fail the result immediately
      case Failure(cause) => result tryFailure cause
      // If all futures have succeeded, complete successful result
      case Success(_) if remaining == 0 => 
        result tryCompleteWith Future.sequence(futures)
      case _ =>
    }
  }

  futures.foreach(_ onComplete attemptComplete)
  result.future
}

ScalaZ在内部执行类似的操作,因此 f1 | @ | f2 列表(f1,f2).sequence 在任何期货失败后立即失败。

ScalaZ does a similar thing internally, so both f1 |@| f2 and List(f1, f2).sequence fail immediately after any of the futures fails.

下面是这些方法的失败时间的快速测试:

Here is a quick test of the failing time for those methods:

import java.util.Date
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scalaz._, Scalaz._

object ReflectionTest extends App {
  def f1: Future[Unit] = Future {
    Thread.sleep(2000)
  }

  def f2: Future[Unit] = Future {
    Thread.sleep(1000)
    throw new RuntimeException("Failure")
  }

  def test(name: String)(
    f: (Future[Unit], Future[Unit]) => Future[Unit]
  ): Unit = {
    val start = new Date().getTime
    f(f1, f2).andThen {
      case _ => 
        println(s"Test $name completed in ${new Date().getTime - start}")
    }
    Thread.sleep(2200)
  }

  test("monadic") { (f1, f2) => for (v1 <- f1; v2 <- f2) yield () }

  test("zip") { (f1, f2) => (f1 zip f2).map(_ => ()) }

  test("Future.sequence") { 
    (f1, f2) => Future.sequence(Seq(f1, f2)).map(_ => ()) 
  }

  test("smartSequence") { (f1, f2) => smartSequence(Seq(f1, f2)).map(_ => ())}

  test("scalaz |@|") { (f1, f2) => (f1 |@| f2) { case _ => ()}}

  test("scalaz sequence") { (f1, f2) => List(f1, f2).sequence.map(_ => ())}

  Thread.sleep(30000)
}

我的机器上的结果是:

Test monadic completed in 2281
Test zip completed in 2008
Test Future.sequence completed in 2007
Test smartSequence completed in 1005
Test scalaz |@| completed in 1003
Test scalaz sequence completed in 1005

这篇关于如何在Scala中实现未来作为应用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆