等待Scala中组成Option的期货列表

时间:2015-06-17 17:37:21

标签: scala future

我必须从带有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)所期望的类型。如果findLookUpId无法找到lookUp ID(即None),我想忽略Future[Option[X]]的结果。我在各种教程中读到null在函数组合和Scala中的表达式中不容易处理。所以我也很好奇最佳实践是如何妥善处理这些问题。

其次,我不得不等待所有期货完成,但不知道如何将结果返回到调用Java框架(2)。我可以在这里使用承诺来实现这一目标吗?如果是,我该怎么办?

最后但并非最不重要的是,如果有任何错误,我只想从thisIsCalledByJavaFramework返回==,但不知道如何(3)。

非常感谢任何帮助。

谢谢, 迈克尔

1 个答案:

答案 0 :(得分:1)

有几点:

  • (1)中的第一个问题是,您无法处理findLookupId返回None的情况。在这种情况下,您需要决定做什么。整个过程失败了吗?从列表中排除该文件?
  • (1)中的第二个问题是findIssues本身会返回Future,您需要map才能构建结果元组
  • map然后Future.sequence的快捷方式:Future.traverse
  • 如果您无法更改方法的结果类型,因为Java接口已修复且无法更改为支持Futures本身,则必须等待Future完成。使用Await.readyAwait.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
  }
}