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

如果另一个未来失败了,如何取消未来行动?

许黎明
2023-03-14

我有2个期货(数据库表上的2个操作),我希望在保存修改之前检查两个期货是否都成功完成。

现在,我开始第二个未来在第一个(作为依赖),但我知道这不是最好的选择。我知道我可以使用一个理解来并行执行两个期货,但即使一个失败,另一个也会被执行(尚未测试)

firstFuture.dropColumn(tableName) match {
  case Success(_) => secondFuture.deleteEntity(entity)
  case Failure(e) => throw new Exception(e.getMessage)
}

// the  first future alters a table, drops a column
// the second future deletes a row from another table

在这种情况下,如果第一个未来成功执行,则第二个未来可能会失败。我想恢复第一个未来的更新。我听说过SQL事务,似乎是这样的东西,但是如何呢?

val futuresResult = for {
  first <- firstFuture.dropColumn(tableName)
  second <- secondFuture.deleteEntity(entity)
} yield (first, second)

在我的情况下,理解要好得多,因为我在这两个未来之间没有依赖关系,并且可以并行执行,但这并不能解决我的问题,结果可能是(成功,成功)或(失败,成功)例如。

共有1个答案

董嘉祯
2023-03-14

这有点棘手,因为ScalaFuture被设计为渴望。在各种Scala库中还有一些其他结构可以处理同步和异步效果,例如catIO、MonixTaskZIO等,它们以懒惰的方式设计,它们没有这种行为。

< code>Future急切的原因是它会尽快开始计算。这里的“start”是指在显式选择或隐式显示的< code>ExecutionContext上调度它。虽然在调度程序决定停止执行的情况下,执行在技术上可能会有一点停顿,但它很可能几乎是瞬间开始的。

因此,如果您有一个类型为<code>Future</code>的值,它将从那时开始运行。如果您有一个类型为Future的惰性值,或者一个返回类型为Future>/code>值的函数/方法,那么它不是。

但是,即使您拥有的只是简单的值(没有惰性变量或def),如果Future定义是在for理解中完成的,那么这意味着它是一元平面映射链的一部分(如果您不理解这一点,请暂时忽略它),并且它将按顺序运行,而不是并行运行。为什么?这并不是Futures特有的;每个for理解都具有作为顺序链的语义学,您可以在其中将上一步的结果传递到下一步。因此,如果它依赖于步骤n中的某些内容,那么您不能在步骤n 1中运行某些内容是合乎逻辑的。

这里有一些代码来演示这一点。

val program = for {
  _ <- Future { Thread.sleep(5000); println("f1") }
  _ <- Future { Thread.sleep(5000); println("f2") }
} yield ()

Await.result(program, Duration.Inf)

程序将等待五秒钟,然后打印“f1”,再等待五秒钟,然后打印“f2”。

现在让我们来看看这个:

val f1 = Future { Thread.sleep(5000); println("f1") }
val f2 = Future { Thread.sleep(5000); println("f2") }

val program = for {
  _ <- f1
  _ <- f2
} yield ()

Await.result(program, Duration.Inf)

然而,该程序将在五秒钟后同时打印“f1”和“f2”。

请注意,在第二种情况下并没有真正违反序列语义学。f2仍然有机会使用f1的结果。但是f2没有使用f1的结果;它是一个可以立即计算的独立值(使用val定义)。因此,如果我们将val f2更改为函数,例如def f2(number: Int),则执行更改:

val f1 = Future { Thread.sleep(5000); println("f1"); 42 }
def f2(number: Int) = Future { Thread.sleep(5000); println(number) }

val program = for {
  number <- f1
  _ <- f2(number)
} yield ()

正如您所料,这将在五秒后打印“f1”,只有到那时另一个Future才会启动,因此它将在另外五秒后打印“42”。

正如注释中提到的@cbley,这听起来像是你想要数据库事务。例如,在SQL数据库中,这具有非常特定的含义,并确保ACID属性。

如果这是你需要的,你需要在数据库层上解决它。未来太笼统了。它只是一种对同步和异步计算进行建模的效果类型。当您看到 Future 值时,仅通过查看类型,您无法判断它是数据库调用的结果,还是某个 HTTP 调用的结果。

例如,doobie 将每个数据库查询描述为 ConnectionIO 类型。您可以将多个查询排成一行以供理解,就像您对Future一样:

val program = for {
  a <- database.getA()
  _ <- database.write("foo")
  b <- database.getB()
} yield {
  // use a and b
}

但与我们之前的示例不同,这里的< code>getA()和< code>getB()不返回< code>Future[A]类型的值,而是< code>ConnectionIO[A]。最酷的是,doobie完全考虑到您可能希望这些查询在单个事务中运行,因此如果< code>getB()失败,“foo”将不会提交给数据库。

因此,在这种情况下,您要做的是获得查询集的完整描述,将其封装到一个类型为connectiono的单个值程序中,一旦您想要实际运行事务,您将执行类似的操作。transact(MyTransact),其中<code>MyTransact</code>是<code>transact的实例,这是一个知道如何连接到物理数据库的doobie构造。

一旦您进行交易,您的连接[A]将变成未来[A]。如果事务失败,您将有一个失败的未来,并且不会向您的数据库提交任何内容。

如果您的数据库操作彼此独立并且可以并行运行,则 doobie 也允许您执行此操作。通过 doobie 提交事务,无论是按顺序还是并行,在文档中都有很好的解释。

 类似资料:
  • AFAIK将/提交到是我想并行执行资源密集型代码的方法。因此,我的方法结构: 我在上面的代码中标记了两个可能发生故障的点。对于这两种情况,可用于错误处理的选项非常不同。 在提交任务之前,可能会出现一些问题,例如参数无效,一些可能失败的快速预处理代码。 我在这里看到了几种表示失败的方式: 如果提供给的无效立即返回null。在这种情况下,我必须检查每次调用时是否返回null。 抛出检查过的异常而不是上

  • 问题内容: 信封:Akka 2.1,scala版本2.10.M6,JDK 1.7,u5 现在是我的问题:我有: 现在在第一行中,我有一个Future对象的Future,有什么方法可以在不阻塞当前线程的情况下将其转换为Future? Akka有什么方法吗?据我检查,我还没有发现…第一次发帖....不好意思的格式和组织…:〜P 问题答案: 简短答案(英语):flatMap dat sh!t 较短的答案

  • 我正在寻找一种将任意长度的期货列表转换为期货列表的方法。我使用的是Playframework,所以最终,我真正想要的是一个<code>未来〔结果〕,但为了让事情更简单,让我们说<code>将来〔List[Int]]通常的方法是使用<code>Future.sequence(…) 例如,执行以下操作不起作用: 我希望能够将1和3从那里拉出来,而不是只得到异常。我尝试将来使用<code>。折叠,但这显

  • 在Scala中,未来可能会失败,这可以异步发现: 你将如何“翻译”成Clojure?我的阅读让我相信Clojure未来/promise模型没有Scala的强大,你不能就这样抓住失败。那么该怎么办呢? Scala的未来永远不需要被问到它的价值——当它好并准备好时,它会告诉你发生了什么(包括它是否失败——这是这个问题的关键)。这就是我所说的“异步”的意思。Scala的未来可以处于三种可能的状态之一——

  • 问题内容: 我正在使用Java 8可完成的期货。我有以下代码: 我使用runAsync计划执行等待闩锁的代码。接下来,我取消了将来,希望将被中断的异常抛出内部。但是似乎线程在await调用上仍然处于阻塞状态,即使将来被取消(断言通过)也永远不会抛出InterruptedException。使用ExecutorService的等效代码可以正常工作。是CompletableFuture中的错误还是我的

  • 我有一个Scala future,它调用一个api并返回future,如果结果不正确,那么另一个api调用将与第一个future的结果一起提交并作为future返回。 这是我目前为止所拥有的。 但是如果我访问fut2结果,它会给出这样的结果: 有没有一种方法,我可以选择返回fot2,如果fot1的结果是不准确的? 编辑:第二个未来必须使用第一个未来来继续API调用。这就是我到目前为止所拥有的。