当前位置: 首页 > 知识库问答 >
问题:

Scala期货-如何从两个期货中获得结果或失败

松雅昶
2023-03-14

我使用 for 并行运行 2 个期货。我想知道在所有情况下哪个成功了,哪个失败了(所有情况都应该运行到完成,并显示结果或失败状态)。目前,我只能检索到综合成功结果

我从这里开始操作,但这还不够,因为我无法获得单个失败时的成功状态,也无法获得两个失败时的失败状态。在Scala未来的理解中,两个失败都是失败的

case class TaggedException(context:String, val throwable: Throwable) extends Exception(throwable.getMessage)

val f1 = Future {...}.recoverWith {case e:Throwable => Future.Failed(new TaggedException("first one failed", e))}
val f2 = Future {...}.recoverWith {case e: Throwable => Future.Failed(new TaggedException("second one failed", e))}

val combinedResult = for {
  r1 <- f1
  r2 <- f2
} yield (r1,r2)

combinedResult.onFailure {
case e : TaggedException => ... // if both fail I only get the first line in the for
// in case where single fails I only know fail status without the success of the second
}

我试图避免这种混乱:

var countCompleted = 0 ... or some other atomic way to count 
f1 onComplete {
  case Success(value) => {
    ... countCompleted increment ... 
    // handle success
    if both completed {
       // handle returning a status
    }
  }
  case Failure(error) => {
    ... countCompleted increment ... 
    // handle failure
    if both completed {
       // handle returning a status
    }
  }
}

f2 onComplete {
  case Success(value) => {
    ... countCompleted increment ... 
    // handle success
    if both completed {
       // handle returning a status
    }
  }
  case Failure(error) => {
    ... countCompleted increment ... 
    // handle failure
    if both completed {
       // handle returning a status
    }
  }
}

编辑:另一个版本-这是一个有效的方法吗?

def toFutureTry[A](future: Future[A]):Future[Try[A]] = future.map(Success(_)).recover {case t: Throwable => Failure(t)}

    val fa: Future[Try[Blah]] = toFutureTry(f1)
    val fb: Future[Try[Foo]] = toFutureTry(f2)

    val combinedRes = for {
      ra <- fa
      rb <- fb
    } yield (ra,rb)

    combinedRes.onComplete {
      case Success(successRes: (Try[Blah], Try[Foo])) => // all of these cases are success or fails
      case Failure(f: Throwable) => // i think it is unused right?
    }

共有3个答案

钱渊
2023-03-14

对于理解,一旦一行失败,代码就会停止在那里并抛出任何异常。如果r1

我个人会把它们都放在<code>中,或者[可丢弃的,随便什么],而不是把它们都放到<code>里。在将来使用进行恢复。失败(…)。通过这种方式,您可以使用您获得的任何值执行操作,而不仅仅是执行onFailure,还可以使用您得到的任何执行其他操作。或者您可以使用<code>Try/Success/Failure</code>…但这取决于您想如何处理错误。

我不知道您的具体用例,但如果您想单独处理每个成功或失败的案例,您可以这样做:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Failure, Success, Try}

val f1 = Future(Try(1 / 1))
val f2 = Future(Try(1 / 0))

// for-comprehensions won't fall over when reading a Failure
// as failures don't extend Throwable
val combinedResult = for {
  r1 <- f1 // Success(1)
  r2 <- f2 // Failure(java.lang.ArithmeticException: / by zero)
} yield List(r1,r2)

combinedResult.map { // get inside Future
  f =>
    f.map { // get inside List
      case Success(a) => // do something with success
      case Failure(e: IndexOutOfBoundsException) => // do something with failure
      case Failure(e: ArithmeticException) => // do something with failure
      case Failure(e) => // do something with failure
    }
}

我个人不喜欢使用onComplete我更喜欢通过映射将数据尽可能长地保存在未来中。不过这只是个人喜好。

夹谷星纬
2023-03-14

Future[A]上使用FLMap不会有帮助,因为它总是会在其中一个产生的第一个故障时短路,您真的想在其中积累错误。

使用 Future.traverse 的解决方案,它将适用于任意多个 Future[A] 实例:

val f1 = Future.failed[Int](new Exception("42")).recoverWith {
  case e: Throwable => Future.failed(TaggedException("first one failed", e))
}

val f2 = Future(42).recoverWith {
  case e: Throwable =>
    Future.failed(TaggedException("second one failed", e))
}

val res: Future[List[Either[Throwable, Int]]] = 
  Future
   .traverse(List(f1, f2)) {
      eventualInt => eventualInt
       .map(i => Right(i))
       .recover { case err => Left(err) }
   }

res.onComplete {
  case Failure(exception) =>
    println(exception)
  case Success(value) =>
    value.foreach {
      case Right(int) => println(s"Received num: $int")
      case Left(err) => println(s"Oh no, err: $err")
    }
}

Await.result(res, Duration.Inf)

我们还可以使用猫对其验证类型的一点帮助:

import cats.data.Validated.{Invalid, Valid}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import cats.implicits._
import scala.util.{Failure, Success}

def main(args: Array[String]): Unit = {
  case class TaggedException(context: String, throwable: Throwable)
    extends Exception(throwable.getMessage)

  val f1 = Future.failed[Int](new Exception("42")).recoverWith {
    case e: Throwable => Future.failed(TaggedException("first one failed", e))
  }

  val f2 = Future(42).recoverWith {
    case e: Throwable => Future.failed(TaggedException("second one failed", e))
  }

  val res: Future[List[Validated[Throwable, Int]]] = 
    List(f1, f2)
     .traverse(eventualInt => eventualInt
                       .map(i => Valid(i))
                       .recover { case err => Invalid(err) })

  res.onComplete {
    case Failure(exception) =>
      println(exception)
    case Success(value) =>
      value.foreach {
        case Valid(int) => println(s"Received num: $int")
        case Invalid(err) => println(s"Oh no, err: $err")
      }
  }

  Await.result(res, Duration.Inf)
}

将屈服:

Oh no, err: TaggedException$3: 42
Received num: 42
公西苗宣
2023-03-14

您可以像这样组合< code>transform和< code>zip:

val combinedResult: Future[(Try[T], Try[T])] =
  f1.transform(Success(_)).zip(f2.transform(Success(_)))

然后你可以做:

combinedResult map {
  case (Success(v1), Success(v2)) =>
  case (Success(v1), Failure(f2)) =>
  case (Failure(f1), Success(v2)) =>
  case (Failure(f1), Failure(f2)) =>
}
 类似资料:
  • 我正在使用: Scala 2.10 游戏2.1 目前,我正在使用 类,但我愿意尝试另一个 API。 我很难将多个期货的结果组合成一个列表[(String, String)]。 以下 方法成功地将单个 Future 的结果返回到 HTML 模板: 方法执行 Web 服务 API 调用并返回 Future[play.api.libs.ws.Response]。方法 向 HTML 模板返回 List[(

  • 我有期货清单 我需要的是一种获取结果列表的方法 你能在java中提供解决方案吗? 类似于

  • 在这种情况下,我的做法是使用 将 转换为 。然后使用 获取结果。但是,可能有一个任务需要很长时间并且超时。在这种情况下,我仍然希望获得其余结果(同时并行运行所有任务)。可能吗?怎么办? 谢谢

  • 我试图在我正在编写的脚本中测试错误处理。如果异步函数fetchBar失败,我将模式匹配失败案例,然后返回包含失败结果的成功未来。 然而,当我对这个流进行单元测试时,我在测试失败案例时遇到了麻烦。我在fetchBar上打了一个存根,以返回失败的future,如下所示。 但是我注意到fetchedBar返回的是成功而不是失败。为什么会这样,我如何存根fetchBar函数来创建一个失败的尝试?

  • 我有两个在未来发生的计算,如下所示: 我希望它能够运行,以便comp2总是在comp1完成后运行!我知道使用一个表达,我可以组成这两个Future的喜欢。 什么能保证comp1在comp2之前完成?我的意思是这些是发生在不同线程中的计算,并且不能保证运行的顺序。有没有一种方法可以保证没有阻塞的顺序?

  • 我想把这个val: 对它进行一些操作(我正在考虑展平) 然后得到这个结果 如果展平方法在这里不合适,那很好。只要我得到结果。 谢啦!