阻止未来有什么意义吗?

Is there any point in blocking for a future?

假设我有一个应用程序处理很多请求。其中一个请求需要一段时间才能完成。我有以下代码:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent.Await
import scala.concurrent.Future

def longReq(data:String):String = {
  val respFuture = Future{ 
     // some code that computes resp but takes a long time
     // can my application process other requests during this time?
     resp = ???  // time-consuming step
  }
  Await.result(respFuture, 2 minutes)
}

如果我根本不使用 futures,应用程序将被阻塞,直到 resp 被计算,并且在此期间无法并行处理其他请求。但是,如果我使用 futures 然后使用 Await 阻塞 resp,应用程序是否能够在计算 resp 时并行处理其他请求?

在您的特定示例中,假设 longReq 被请求循环连续调用,答案是 不,它无法处理任何其他内容。为此 longReq 将不得不 return 一个未来:

def longReq(data:String): Future[String] = {
  Future { 
     // some code that computes resp but takes a long time
     // can my application process other requests during this time?
     resp = ???  // time-consuming step
  }
}

当然,这只是将您可能使用 Await.result 的原因进一步推向了底线。

使用Future的目的是为了避免阻塞,但它是海龟一路向下买入。如果您想使用 Future,最终接收者必须能够以 异步 方式处理获取结果,即您的请求循环必须有一种方法来捕获以这样的方式调用者,当未来最终完成时,调用者可以被告知结果

让我们假设你的请求循环接收到一个 response 回调的请求对象,然后你会像这样调用 longReq (假设使用 longReq 那 return一个 Future):

def asyncCall(request: Request): Unit = {
  longReq(request.data).map( result => request.response(result) )
}

最常见的使用流程的场景是 HTTP 或其他服务器,其中同步 Request => Response 周期具有 async 等价于 Request => Future[Response],几乎任何现代服务器框架都提供(Play、Finatra、Scalatra 等)

什么时候使用Await.result

一种情况下,使用 Await.result 可能是合理的,如果你有一堆 Future 并且愿意在全部完成时阻止(假设使用 longReq 那 return 是 Future):

 val futures = allData.map(longReq))                // List[Future[String]]
 val combined = Future.sequence(futures)            // Future[List[String]]
 val responses = Await.result(combined, 10.seconds) // List[String]

当然,combined 是一个 Futuremap 并异步处理结果会更好