我使用Scala向API (确切地说,是Play Framework的WS )发出HTTP请求,该API用一个JSON响应来响应,类似于;
{
data: [
{text: "Hello there", id: 1},
{text: "Hello there again", id: 2}
],
next_url: 'http://request-this-for-more.com/api?page=2' //optional
}
因此,返回的JSON中的next_url
字段可能存在,也可能不存在。
我的方法需要做的是从调用第一个URL开始,检查响应是否有next_url
,然后对其执行GET。最后,我应该将来自响应的所有data
字段组合成所有数据字段的一个未来。当响应中没有next_url
时,我将终止它。
现在,用阻塞的方式做这件事比较容易,但我不想这样做。解决这样的问题最好的办法是什么?
发布于 2015-05-22 01:33:09
在scalaz中可能有这样的方法,但是如果您不知道一个特定的解决方案,通常可以使用递归和flatMap
来构造一个解决方案。类似于:
//Assume we have an async fetch method that returns Result and Option of next Url
def fetch(url: Url): Future[(Result, Option[Url])] = ...
//Then we can define fetchAll with recursion:
def fetchAll(url: Url): Future[Vector[Result]] =
fetch(url) flatMap {
case (result, None) => Future.successful(Vector(result))
case (result, Some(nextUrl)) =>
fetchAll(nextUrl) map {results => result +: results}
}
(请注意,这对每个调用都使用了一个堆栈框架-如果您想要执行数千次获取,那么我们需要更仔细地编写它,以便它是尾递归的)
发布于 2015-05-22 01:33:39
这种情况下,Future.flatMap方法确实存在。
假设你有这样的东西:
case class Data(...)
def getContent(url:String):Future[String]
def parseJson(source:String):Try[JsValue]
def getData(value: JsValue):Seq[Data]
JsValue
类型有受播放json图书馆启发的方法
def \ (fieldName: String): JsValue
def as[T](implicit ...):T //probably throwing exception
你可以写最后的结果
def innerContent(url:String):Future[Seq[Data]] = for {
first <- getContent(url)
json <- Future.fromTry(parseJson(first))
nextUrlAttempt = Try((json \ "next_url").as[String])
dataAttempt = Try(getData(json \ "data"))
data <- Future.fromTry(dataAttempt)
result <- nextUrlAttempt match {
case Success(nextUrl) => innerContent(nextUrl)
case Failure(_) => Future.successful(Seq())
} yield data ++ result
还可以查看针对像您这样的复杂异步流的库:
https://stackoverflow.com/questions/30392773
复制相似问题