将来如何使用 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
调用中建立 Seq
个 Future
。然后 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)
}
我使用 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
调用中建立 Seq
个 Future
。然后 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)
}