等待在 Scala 中组合 Option 的期货列表
Wait for a list of futures with composing Option in Scala
我必须使用 Scala 从 REST API 中获取给定列表中每个文件的问题列表。我想并行执行请求,并为此使用 Dispatch 库。我的方法是从 Java 框架调用的,我必须在此方法结束时等待所有期货的结果将总体结果返回给框架。这是我的代码:
def fetchResourceAsJson(filePath: String): dispatch.Future[json4s.JValue]
def extractLookupId(json: org.json4s.JValue): Option[String]
def findLookupId(filePath: String): Future[Option[String]] =
for (json <- fetchResourceAsJson(filePath))
yield extractLookupId(json)
def searchIssuesJson(lookupId: String): Future[json4s.JValue]
def extractIssues(json: org.json4s.JValue): Seq[Issue]
def findIssues(lookupId: String): Future[Seq[Issue]] =
for (json <- searchIssuesJson(componentId))
yield extractIssues(json)
def getFilePathsToProcess: List[String]
def thisIsCalledByJavaFramework(): java.util.Map[String, java.util.List[Issue]] = {
val finalResultPromise = Promise[Map[String, Seq[Issue]]]()
// (1) inferred type of issuesByFile not as expected, cannot get
// the type system happy, would like to have Seq[Future[(String, Seq[Issue])]]
val issuesByFile = getFilePathsToProcess map { f =>
findLookupId(f).flatMap { lookupId =>
(f, findIssues(lookupId)) // I want to yield a tuple (String, Seq[Issue]) here
}
}
Future.sequence(issuesByFile) onComplete {
case Success(x) => finalResultPromise.success(x) // (2) how to return x here?
case Failure(x) => // (3) how to return null from here?
}
//TODO transform finalResultPromise to Java Map
}
此代码段有几个问题。首先,我没有得到我期望 issuesByFile
(1) 的类型。如果无法找到查找 ID(即 None
),我只想忽略 findLookUpId
的结果。我在各种教程中读到 Future[Option[X]]
在函数组合和 Scala 中的表达式中不容易处理。所以我也很好奇正确处理这些问题的最佳做法是什么。
其次,我不得不等待所有 futures 完成,但不知道如何 return 调用 Java 框架的结果 (2)。我可以在这里使用承诺来实现这一目标吗?如果是,我该怎么做?
最后但同样重要的是,如果有任何错误,我只想从 thisIsCalledByJavaFramework
return null
但不知道如何 (3).
非常感谢任何帮助。
谢谢,
迈克尔
几点:
- (1) 处的第一个问题是您没有处理
findLookupId
returns None
的情况。在这种情况下,您需要决定要做什么。整个过程失败?从列表中排除该文件?
- (1) 处的第二个问题是
findIssues
本身会 return 一个 Future
,在构建结果元组之前你需要 map
=30=]
map
和 Future.sequence
有一个快捷方式:Future.traverse
- 如果您不能更改方法的结果类型,因为 Java 接口是固定的,不能更改以支持 Futures 本身,您必须等待 Future 完成。使用
Await.ready
或 Await.result
来做到这一点。
考虑到所有这些因素并选择忽略无法找到 ID 的文件会导致此代码:
// `None` in an entry for a file means that no id could be found
def entryForFile(file: String): Future[(String, Option[Seq[Issue]])] =
findLookupId(file).flatMap {
// the need for this kind of pattern match shows
// the difficulty of working with `Future[Option[T]]`
case Some(id) ⇒ findIssues(id).map(issues ⇒ file -> Some(issues))
case None ⇒ Future.successful(file -> None)
}
def thisIsCalledByJavaFramework(): java.util.Map[String, java.util.List[Issue]] = {
val issuesByFile: Future[Seq[(String, Option[Seq[Issue]])]] =
Future.traverse(getFilePathsToProcess)(entryForFile)
import scala.collection.JavaConverters._
try
Await.result(issuesByFile, 10.seconds)
.collect {
// here we choose to ignore entries where no id could be found
case (f, Some(issues)) ⇒ f -> issues
}
.toMap.mapValues(_.asJava).asJava
catch {
case NonFatal(_) ⇒ null
}
}
我必须使用 Scala 从 REST API 中获取给定列表中每个文件的问题列表。我想并行执行请求,并为此使用 Dispatch 库。我的方法是从 Java 框架调用的,我必须在此方法结束时等待所有期货的结果将总体结果返回给框架。这是我的代码:
def fetchResourceAsJson(filePath: String): dispatch.Future[json4s.JValue]
def extractLookupId(json: org.json4s.JValue): Option[String]
def findLookupId(filePath: String): Future[Option[String]] =
for (json <- fetchResourceAsJson(filePath))
yield extractLookupId(json)
def searchIssuesJson(lookupId: String): Future[json4s.JValue]
def extractIssues(json: org.json4s.JValue): Seq[Issue]
def findIssues(lookupId: String): Future[Seq[Issue]] =
for (json <- searchIssuesJson(componentId))
yield extractIssues(json)
def getFilePathsToProcess: List[String]
def thisIsCalledByJavaFramework(): java.util.Map[String, java.util.List[Issue]] = {
val finalResultPromise = Promise[Map[String, Seq[Issue]]]()
// (1) inferred type of issuesByFile not as expected, cannot get
// the type system happy, would like to have Seq[Future[(String, Seq[Issue])]]
val issuesByFile = getFilePathsToProcess map { f =>
findLookupId(f).flatMap { lookupId =>
(f, findIssues(lookupId)) // I want to yield a tuple (String, Seq[Issue]) here
}
}
Future.sequence(issuesByFile) onComplete {
case Success(x) => finalResultPromise.success(x) // (2) how to return x here?
case Failure(x) => // (3) how to return null from here?
}
//TODO transform finalResultPromise to Java Map
}
此代码段有几个问题。首先,我没有得到我期望 issuesByFile
(1) 的类型。如果无法找到查找 ID(即 None
),我只想忽略 findLookUpId
的结果。我在各种教程中读到 Future[Option[X]]
在函数组合和 Scala 中的表达式中不容易处理。所以我也很好奇正确处理这些问题的最佳做法是什么。
其次,我不得不等待所有 futures 完成,但不知道如何 return 调用 Java 框架的结果 (2)。我可以在这里使用承诺来实现这一目标吗?如果是,我该怎么做?
最后但同样重要的是,如果有任何错误,我只想从 thisIsCalledByJavaFramework
return null
但不知道如何 (3).
非常感谢任何帮助。
谢谢, 迈克尔
几点:
- (1) 处的第一个问题是您没有处理
findLookupId
returnsNone
的情况。在这种情况下,您需要决定要做什么。整个过程失败?从列表中排除该文件? - (1) 处的第二个问题是
findIssues
本身会 return 一个Future
,在构建结果元组之前你需要map
=30=] map
和Future.sequence
有一个快捷方式:Future.traverse
- 如果您不能更改方法的结果类型,因为 Java 接口是固定的,不能更改以支持 Futures 本身,您必须等待 Future 完成。使用
Await.ready
或Await.result
来做到这一点。
考虑到所有这些因素并选择忽略无法找到 ID 的文件会导致此代码:
// `None` in an entry for a file means that no id could be found
def entryForFile(file: String): Future[(String, Option[Seq[Issue]])] =
findLookupId(file).flatMap {
// the need for this kind of pattern match shows
// the difficulty of working with `Future[Option[T]]`
case Some(id) ⇒ findIssues(id).map(issues ⇒ file -> Some(issues))
case None ⇒ Future.successful(file -> None)
}
def thisIsCalledByJavaFramework(): java.util.Map[String, java.util.List[Issue]] = {
val issuesByFile: Future[Seq[(String, Option[Seq[Issue]])]] =
Future.traverse(getFilePathsToProcess)(entryForFile)
import scala.collection.JavaConverters._
try
Await.result(issuesByFile, 10.seconds)
.collect {
// here we choose to ignore entries where no id could be found
case (f, Some(issues)) ⇒ f -> issues
}
.toMap.mapValues(_.asJava).asJava
catch {
case NonFatal(_) ⇒ null
}
}