我有一个场景,我得到一个String消息列表,我必须遍历String并调用另一个方法,这是一个长时间运行的过程。然后我必须收集这个长时间运行的进程的结果并连接结果并将其发送回用户界面。我对Scala中的这些Future概念很陌生。我正在使用Play框架,其中字符串列表将来自用户界面。以下是我第一次尝试实现ht场景的方法:
def futuresTest(strList: List[String]) = Action {
Async {
val ftrList: List[Future[String]] =
strList.map(s => Akka.future {longRunningCall(s)} ).toList
val futureResultList = Future.sequence(ftrList)
val jsonResponse: String =
futureResultList.map(_.sum).asInstanceOf[String]
Akka.future { Ok(jsonResponse) }
}
}
为简单起见,longRunningCall只返回一个String。稍后我将把它与最初的实现联系起来。
def longRunningCall(s: String) = "test"
我的问题是在声明中:
val ftrList: List[Future[String]] =
strList.map(s => Akka.future {longRunningCall(s)} ).toList
我认为ftrList将异步填充,当它到达下一行时,我保证futureResultList将包含所有元素(即strList和futureResultList大小相等?
val futureResultList = Future.sequence(ftrList)
请指教!
答案 0 :(得分:2)
我将在这里假设你的意思是连接字符串。首先对代码进行一些评论:
没有必要将整个块包装在异步中,只是你要返回的最后一个。
可以推断出所有值类型,您无需明确说明它们
映射List
会返回List
。在结果上调用toList
是多余的。
List#sum
仅适用于数字,请使用foldLeft
代替字符串。
Future[String]
可以 直接通过String
投放到asInstanceOf
。无论如何,你将以Future
的形式返回。
map
和Future.sequence
可以合并为一个Future.traverse
操作。
更改代码以应用这些要点:
def futuresTest(strList: List[String]) = Action {
val ftrList = Future.traverse(strList) {
s => Future( longRunningCall(s) )
}
// this will be a Future[String]
val jsonResponse = ftrList map { _.foldLeft("")(_ + _) }
Async { jsonResponse map (OK(_)) }
}
最后两行也可以合并:
Async {
ftrList map { xs => OK(xs.foldLeft("")(_ + _)) }
}
<强>更新强>
使用Future.fold也是如此,正如Viktor所建议的
def futuresTest(strList: List[String]) = Action {
val ftrList = strList map { longRunningCall(_) } // List[Future]
val jsonResponse = Future.fold(ftrList)("")(_ + _) // Future[String]
Async { jsonResponse map (OK(_)) }
}
为了处理失败,您希望恢复未来并让它返回不同的响应:
Async { jsonResponse map (OK(_)) recover (InternalServerError(_.toString)) }
如果你想处理每个元素的个别错误,那么你应该看看this answer中使用的技术。
答案 1 :(得分:0)
只要longRunningCall正确返回值,您正在做的事情就会正常工作。除非来自longRunningCall
的异常,否则ftrList大小将等于strList大小答案 2 :(得分:0)
在我看来,对于你想要的东西,只使用Scala的Parallel系列而不是Futures会更简单。通过使用.par.map
,您可以并行执行列表中每个项目的长时间运行操作,并一起收集结果。
def getResponse(strList: List[String]) = Action {
val results: List[String] = strList.par.map(longRunningCall(_))
val jsonResponse: String = results.mkString(",") //concatenate using ','
Ok(jsonResponse)
}
http://docs.scala-lang.org/overviews/parallel-collections/configuration.html