【问题标题】:First for-callback prints future list almost every time, second onComplete-callback rarely returns success (never failure)第一个 for-callback 几乎每次都打印未来列表,第二个 onComplete-callback 很少返回成功(从不失败)
【发布时间】:2025-11-29 18:15:01
【问题描述】:

我正在浏览一些未来的例子。我有一个 Future 从方法中检索列表。我调用两种不同类型的回调,一个 Foreach 回调和一个 onComplete 回调只是为了尝试一下。

  • Foreach 回调几乎每次都返回列表。
  • 即使 For 回调已返回列表,onComplete 回调也很少返回 SUCCESS。
  • onComplete 回调永远不会返回 FAILURE。

有人可以向我解释发生了什么吗?

我了解回调确实同时执行,并且没有顺序。但是如果 Future 将列表返回给 Foreach 并且 onComplete 回调在 Foreach 和 Future 之前执行并且它试图从不成功的 Future 获取列表,那么 onComplete 回调不应该返回 FAILURE 吗?

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

object FuturesExampleObj extends App {

  println("Using Future to retrieve list\n")
  val l: Future[List[String]] = Future {
    getList()
  }

  println("FOR CALLBACK --------------------\n")

  l foreach {
    items =>
      for(item <- items) println(s"foreach item : $item")
      println("\n")
  }

  println("onComplete CALLBACK --------------------\n")

  l onComplete {
    case Success(i) => println(s"SUCCESS : $i")
    case Failure(i) => println(s"FAILURE : $i")
  }

  def getList(): List[String] ={
    val list = ("a" :: "b" :: "c" :: "d" :: "e":: Nil)
    list
  }
}

结果示例 1(通用)

Using Future to retrieve list

FOR CALLBACK --------------------

onComplete CALLBACK --------------------

foreach item : a
foreach item : b
foreach item : c
foreach item : d
foreach item : e



Process finished with exit code 0

结果示例 2(不常见)

Using Future to retrieve list

FOR CALLBACK --------------------

onComplete CALLBACK --------------------


Process finished with exit code 0

结果示例 3(非常罕见)

基本上 onComplete 永远不会返回 SUCCESS 或 FAILURE。有时,很少会返回“SUCCESS:” + 列表。

【问题讨论】:

    标签: scala concurrency callback future


    【解决方案1】:

    这是因为您必须明确阻止未来。 在您的情况下,主线程在 onComplete 完成之前终止,有时在 l foreach .. 完成之前终止。

    请补充:

    import scala.concurrent.{Await, Future}
    import scala.concurrent.duration._
    
    val listF = l foreach {
      items =>
        for(item <- items) println(s"foreach item : $item")
        println("\n")
    }
    
    Await.result(listF, 5 seconds)
    

    这样你将等待这个未来的完成。

    如果要等待onComplete,则需要使用Thread.sleep(在onComplete之后添加,例如:

    l onComplete {
       case Success(i) => println(s"SUCCESS : $i")
       case Failure(i) => println(s"FAILURE : $i")
    }
    
    Thread.sleep(3000)
    

    onComplete 在 ExecutionContext 中的某个线程上运行,而 Await 在当前线程上运行,并阻止它直到它完成或超过指定的超时。 因此onComplete 是非阻塞的,Await 是阻塞的。

    使用onComplete,我们不会阻止来自Future 的结果,而是会收到SuccessFailure 的回调。

    Thread.sleep() 正在阻塞我们的主线程,以便我们可以看到未来的异步结果。

    请注意,您不应在生产代码中使用Await.result,它用于测试Future 的输出。 此外,您肯定不会使用Thread.sleep(),而是对未来返回的结果进行“反应”。

    通常,您会有一些 REST API 调用或其他运行并等待未来完成的服务。

    【讨论】:

    • 如果需要Future结果,但是回调没有及时收到结果,怎么做才能保证最终返回结果呢?有没有办法重试?
    • onComplete 不会返回任何未来,这就是为什么你可以在上面做Await。为确保完成,您可以使用 for-comprehensions(在此处查找更多信息 - docs.scala-lang.org/overviews/core/futures.html)。
    • 更正 - onComplete 不会返回任何未来,这就是您不能等待的原因。乐于助人:)
    【解决方案2】:

    原因是 App 的线程在 Future 完成之前完成。

    只需在程序的 and 处添加 Await.ready(l, Duration.Inf).value.get

    这样做只是为了测试!

    【讨论】:

    • 我按照你的建议做了,但我使用了 Thread.sleep()。为什么主线程在退出前不依赖onComplete的完成?
    • 它们不相关。如果你增加Thread.sleep(),它会起作用。
    • 如果我有多个回调需要返回 Future 值,但我不想等待程序,会发生什么?
    • 你可以组合它们并返回一个 Future。检查例如alvinalexander.com/scala/…
    • 好的,谢谢,我去看看。此外,是否会要求单个 Future 有多个回调?我想不出一个场景,但我很好奇。
    最近更新 更多