【问题标题】:How to Promise.allSettled with Scala futures?如何 Promise.allSettled 与 Scala 期货?
【发布时间】:2020-11-05 05:55:35
【问题描述】:

我有两个 scala 期货。我想在两者都完成后执行一项操作,无论它们是否成功完成。 (此外,我希望能够在那时检查这些结果。)

在 Javascript 中,这是Promise.allSettled

Scala 是否提供了一种简单的方法来做到这一点?

最后一点,如果重要的话:我想在 JRuby 应用程序中执行此操作。

【问题讨论】:

  • 你看Future.sequence了吗?

标签: scala promise future


【解决方案1】:

您可以使用transform 方法创建一个Future,它将始终成功并将结果或错误作为Try 对象返回。

def toTry[A](future: Future[A])(implicit ec: ExecutionContext): Future[Try[A]] =
  future.transform(x => Success(x))

要将两个 Future 合二为一,可以使用zip

def settle2[A, B](fa: Future[A], fb: Future[B])(implicit ec: ExecutionContext)
    : Future[(Try[A], Try[B])] =
  toTry(fa).zip(toTry(fb))

如果你想通过这种方式组合任意数量的Futures,可以使用Future.traverse

def allSettled[A](futures: List[Future[A]])(implicit ec: ExecutionContext)
    : Future[List[Try[A]]] = 
  Future.traverse(futures)(toTry(_))

【讨论】:

  • 堆叠 FutureTry 过于复杂/工程化
  • 老实说,这只是一个愚蠢的论点,因为Promise.allSettled 产生了一个与Scala 的Try 类型同构的对象数组。我编写的 allSettled 方法与 JavaScript 中惯用的 Scala 中的 Promise.allSettled 一样接近。
【解决方案2】:

通常在这种情况下,我们使用 Future.sequence 将 Future 的集合转换为一个 Future 以便您可以在其上进行映射,但 Scala 会短路失败的 Future 并且此后不会等待任何事情(Scala 认为失败是所有人的失败),这不适合你的情况。

在这种情况下,您需要将失败的映射到成功,然后执行序列,例如

val settledFuture = Future.sequence(List(future1, future2, ...).map(_.recoverWith { case _ => Future.unit }))
settledFuture.map(//Here it is all settled)

编辑

由于需要保留结果,我们不映射到Future.unit,而是将实际结果映射到另一层Try:

val settledFuture = Future.sequence(
  List(Future(1), Future(throw new Exception))
    .map(_.map(Success(_)).recover(Failure(_)))
  )
settledFuture.map(println(_))
//Output: List(Success(1), Failure(java.lang.Exception))

EDIT2

可以用transform进一步简化:

Future.sequence(listOfFutures.map(_.transform(Success(_))))

【讨论】:

  • 这是一个糟糕的解决方案。如果你以这种方式做事,你就会失去稍后可能相关的异常。你通常会得到一个Any类型的结果,因为这通常是future结果类型和Unit的最小上限,如果future返回Unit,你不知道原来的future是成功还是失败.最重要的是,OP 提到他有两个期货,并且没有理由假设它们属于同一类型,因此您再次失去了类型安全性。此外,Future.sequence(foo.map(bar)) 更好地表示为Future.traverse(foo)(bar)
  • @MatthiasBerndt 添加了保留结果的解决方案
  • 你仍然 a) 丢失结果类型,b) 在一般情况下不知道 Future 是成功还是失败 c) 有 Future.sequence(foo.map(bar)) 反模式和 d) 不能组合 Futures不同的类型,同时保留它们的类型(OP 明确询问了两个 Futures,而不是任意数字)
  • @MatthiasBerndt 以 Throwable 结果成功是非常反模式的。 OP也没有提到他的期货的结果类型应该被保留。无论如何,我们可以在 Scala 中进行模式匹配来匹配类型。在他对 JS Promise.allSettled 的引用中,它返回了所有结果的集合,这正是我的示例所做的。
  • 是否使用Throwable 的子类型作为Future 的成功结果是一个反模式是无关紧要的;像这样的通用代码不应该对此做出任何假设。非密封类型(如Any)上的模式匹配是另一个糟糕的想法,因为编译器无法检查详尽性并且因为它不适用于泛型(例如,无法区分List[Int]List[String] )。
【解决方案3】:

也许您可以使用并发计数器来跟踪已完成的 Futures 的数量,然后在所有 Futures 完成后完成 Promise

def allSettled[T](futures: List[Future[T]]): Future[List[Future[T]]] = {
  val p = Promise[List[Future[T]]]()
  val length = futures.length
  val completedCount = new AtomicInteger(0)
  futures foreach {
    _.onComplete { _ =>
      if (completedCount.incrementAndGet == length) p.trySuccess(futures)
    }
  }
  p.future
}

val futures = List(
  Future(-11),
  Future(throw new Exception("boom")),
  Future(42)
)

allSettled(futures).andThen(println(_))
// Success(List(Future(Success(-11)), Future(Failure(java.lang.Exception: boom)), Future(Success(42))))

scastie

【讨论】:

    猜你喜欢
    • 2014-01-10
    • 2021-06-17
    • 2012-10-17
    • 2019-05-21
    • 1970-01-01
    • 2020-01-20
    • 2019-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多