将来如何使用 Dispatch 和 Scala 分页 REST 调用

How to page REST calls in a future with Dispatch and Scala

我使用 Scala 和 Dispatch 从分页 REST API 中获取 JSON。我在这里将 futures 与 Dispatch 一起使用的原因是因为我想并行执行对 fetchIssuesByFile 的调用,因为该函数可能会导致每个 lookupId (1x findComponentKeyByRest, n x fetchIssuePage,其中 n 是由 REST API) 生成的页数。

到目前为止,这是我的代码:

def fetchIssuePage(componentKey: String, pageIndex: Int): Future[json4s.JValue]

def extractPagingInfo(json: org.json4s.JValue): PagingInfo

def extractIssues(json: org.json4s.JValue): Seq[Issue]

def findAllIssuesByRest(componentKey: String): Future[Seq[Issue]] = {
  Future {
    var paging = PagingInfo(pageIndex = 0, pages = 0)
    val allIssues = mutable.ListBuffer[Issue]()

    do {
      fetchIssuePage(componentKey, paging.pageIndex) onComplete {
        case Success(json) =>
          allIssues ++= extractIssues(json)
          paging = extractPagingInfo(json)
        case _ => //TODO error handling
      }
    } while (paging.pageIndex < paging.pages)

    allIssues // (1)
  }
}

def findComponentKeyByRest(lookupId: String): Future[Option[String]]

def fetchIssuesByFile(lookupId: String): Future[(String, Option[Seq[Issue]])] =
  findComponentKeyByRest(lookupId).flatMap {
    case Some(key) => findAllIssuesByRest(key).map(
      issues => // (2) process issues
    )
    case None => //TODO error handling
  }

实际问题是,当我尝试在 (2) 处处理它们时,我从未从 findAllIssuesByRest (1) 中获得收集的问题(即,问题序列始终为空)。有任何想法吗?此外,分页代码不是很实用,所以我也愿意接受有关如何使用 Scala 改进它的想法。

非常感谢任何帮助。

谢谢, 迈克尔

那是因为 fetchIssuePage returns Future 代码不等待结果。

解决方案是从 fetchIssuePage 调用中建立 SeqFuture。然后 Future.sequence Seq 产生一个单一的未来。 Return 这个代替。当它们全部完成并为您的 flatMap 代码准备好时,这个未来将会启动。

更新:虽然 Michael 很好地理解了上面的内容(见评论),但我认为为了其他读者的利益,我应该放入一个简化得多的代码,只是为了说明这一点:

def fetch(n: Int): Future[Int] = Future { n+1 }

val fetches = Seq(1, 2, 3).map(fetch)
// a simplified version of your while loop, just to illustrate the point

Future.sequence(fetches).flatMap(results => ...)
// where results will be Seq[Int] - i.e., 2, 3, 4

我想你可以这样做:

def findAllIssuesByRest(componentKey: String): Future[Seq[Issue]] = 
  // fetch first page
  fetchIssuePage(componentKey, 0).flatMap { json =>
    val nbPages = extractPagingInfo(json).pages // get the total number of pages
    val firstIssues = extractIssues(json)       // the first issues

    // get the other pages
    Future.sequence((1 to nbPages).map(page => fetchIssuePage(componentKey, page)))
      // get the issues from the other pages
      .map(pages => pages.map(extractIssues))
      // combine first issues with other issues
      .flatMap(issues => (firstIssues +: issues).flatten)
  }