flatMap
和map
是你的朋友! Promise
类型的这两种方法允许将Promise[A]
的结果转换为另一个Promise[B]
。
这里是他们的行动中一个简单的例子(我特意写了明确更多类型的注释比需要,只是为了帮助理解其中的转换发生):
def preferredSongsAndArtist = Action {
// Fetch the list of your preferred songs from Web Service “A”
val songsP: Promise[Response] = WS.url(WS_A).get
val resultP: Promise[List[Something]] = songsP.flatMap { respA =>
val songs: List[Song] = Json.fromJson(respA.json)
// Then, for each song, fetch the artist detail from Web Service “B”
val result: List[Promise[Something]] = songs.map { song =>
val artistP = WS.url(WS_B(song)).get
artistP.map { respB =>
val artist: Artist = Json.fromJson(respB.json)
// Then, generate and return something using the song and artist
val something: Something = generate(song, artist)
something
}
}
Promise.sequence(result) // Transform the List[Promise[Something]] into a Promise[List[Something]]
}
// Then return the result
Async {
resultP.map { things: List[Something] =>
Ok(Json.toJson(things))
}
}
}
没有不必要的类型注释,并使用“理解“符号,你可以写下面更富有表现力的代码:
def preferredSongsAndArtist = Action {
Async {
for {
// Fetch the list of your preferred songs from Web Service “A”
respA <- WS.url(WS_A).get
songs = Json.fromJson[List[Song]](respA.json)
// Then, for each song, fetch the artist detail from Web Service “B”
result <- Promise.sequence(songs.map { song =>
for {
respB <- WS.url(WS_B(song)).get
artist = Json.fromJson[Artist](respB.json)
} yield {
// Then, generate and return something using the song and artist
generate(song, artist)
}
})
// Then return the result
} yield {
Ok(Json.toJson(result))
}
}
}
谢谢你的回答。我尽快分析和测试这个解决方案。 – YoT 2012-04-03 13:35:47
@ julien如果任何Web服务调用超时或返回500,会发生什么情况? getOrElse? – 2012-04-25 08:06:52
该承诺将以[Thrown](http://www.playframework.org/documentation/api/2.0/scala/play/api/libs/concurrent/Thrown.html)值进行兑换。例如,您可以通过使用[extend](http://www.playframework.org/documentation/api/2.0/scala/index.html#play.api.libs.concurrent.Promise)来处理这种情况。 – 2012-04-25 13:28:50