【发布时间】:2016-05-19 21:27:05
【问题描述】:
Scala Future 标准 API 中有 map/flatMap 方法,还有 recover/recoverWith 方法。
为什么没有collectWith?
collect 方法的代码非常简单:
def collect[S](pf: PartialFunction[T, S])(implicit executor: ExecutionContext): Future[S] =
map {
r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
}
collectWith 方法的代码很容易想象:
def collectWith[S](pf: PartialFunction[T, Future[S]])(implicit executor: ExecutionContext): Future[S] =
flatMap {
r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
}
感谢这篇文章,我知道我可以轻松实现它并“扩展”Future 标准 API:http://debasishg.blogspot.fr/2008/02/why-i-like-scalas-lexically-scoped-open.html
我在我的项目中做到了:
class RichFuture[T](future: Future[T]) {
def collectWith[S](pf: PartialFunction[T, Future[S]])(implicit executor: ExecutionContext): Future[S] =
future.flatMap {
r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
}
}
trait WithRichFuture {
implicit def enrichFuture[T](person: Future[T]): RichFuture[T] = new RichFuture(person)
}
也许我的需求不适合在标准 API 中实现它?
这就是我在 Play2 项目中需要这种方法的原因:
def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
def handleResponse: PartialFunction[WSResponse, Future[Car]] = {
case response: WSResponse if response.status == Status.CREATED => Future.successful(response.json.as[Car])
case response: WSResponse
if response.status == Status.BAD_REQUEST && response.json.as[Error].error == "not_the_good_one" =>
createCar(key, eligibleCars.tail)
}
// The "carApiClient.createCar" method just returns the result of the WS API call.
carApiClient.createCar(key, eligibleCars.head).collectWith(handleResponse)
}
如果没有我的 collectWith 方法,我不知道该怎么做。
也许这样做不是正确的方法?
你知道更好的方法吗?
编辑:
对于不需要collectWith 方法的createCar 方法,我可能有更好的解决方案:
def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
for {
mayCar: Option[Car] <- Future.successful(eligibleCars.headOption)
r: WSResponse <- carApiClient.createCar(key, mayCar.get) if mayCar.nonEmpty
createdCar: Car <- Future.successful(r.json.as[Car]) if r.status == Status.CREATED
createdCar: Car <- createCar(key, eligibleCars.tail) if r.status == Status.BAD_REQUEST && r.json.as[Error].error == "not_the_good_one"
} yield createdCar
}
您对第二种解决方案有何看法?
第二次修改:
仅供参考,感谢@Dylan 的回答,这是我的最终解决方案:
def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
def doCall(head: Car, tail: List[Car]): Future[Car] = {
carApiClient
.createCar(key, head)
.flatMap( response =>
response.status match {
case Status.CREATED => Future.successful(response.json.as[Car])
case Status.BAD_REQUEST if response.json.as[Error].error == "not_the_good_one" =>
createCar(key, tail)
}
)
}
eligibleCars match {
case head :: tail => doCall(head, tail)
case Nil => Future.failed(new RuntimeException)
}
}
朱尔斯
【问题讨论】:
标签: scala future standard-library