【问题标题】:Wait for a list of futures with composing Option in Scala等待在 Scala 中组合选项的期货列表
【发布时间】:2015-06-18 15:02:44
【问题描述】:

我必须从带有 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
}

这段代码 sn-p 有几个问题。首先,我没有得到 issuesByFile (1) 所期望的类型。如果无法找到查找 ID(即None),我想忽略findLookUpId 的结果。我在各种教程中读到 Future[Option[X]] 在函数组合和 Scala 中的表达式中不容易处理。所以我也很好奇正确处理这些问题的最佳做法是什么。

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

最后但同样重要的是,如果出现任何错误,我只想从 thisIsCalledByJavaFramework 返回 null,但不知道如何 (3)。

非常感谢任何帮助。

谢谢, 迈克尔

【问题讨论】:

    标签: scala future


    【解决方案1】:

    几点:

    • (1) 处的第一个问题是您没有处理findLookupId 返回None 的情况。您需要决定在这种情况下该怎么做。整个过程失败?从列表中排除该文件?
    • (1) 处的第二个问题是findIssues 本身会返回一个Future,你需要map 才能构建结果元组
    • mapFuture.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
      }
    }
    

    【讨论】:

    • 非常容易理解的解释与工作代码。谢谢!
    猜你喜欢
    • 2015-06-03
    • 2020-06-12
    • 2020-10-31
    • 2013-08-02
    • 2017-07-30
    • 2015-08-24
    • 2021-03-02
    • 2016-05-02
    • 1970-01-01
    相关资源
    最近更新 更多