2017-06-23 19 views
0

大多数时候,我的未来业务依赖于连锁店的前期业务。大部分时间我都在使用flatMap函数和模式匹配。如;如何减少连锁期货的套期保值

findUser(userId).flatMap { 
    case None => Future.successful(NotFound("No user with given id")) 
    case Some(user) => findAddress(user.addressId).flatMap { 
    case None => Future.successful(NotFound("No address with given id")) 
    case Some(address) => findCity(address.cityId).flatMap { 
     case None => Future.successful(NotFound("No city with given id")) 
     case Some => Future.successful(Ok) 
    } 
    } 
} 

用这种方法我能够返回与问题相关的对象,所有分支都在处理中。但在我看来这种方法的缺点(和我的代码阅读乐趣),它嵌套了很多。此外,如果行数太长,就不可能跟踪哪种情况下的声明是哪个格式正确的。这就是编辑器的右下角。

建议的另一种方式可能会用于理解。下面是上面代码的一个等同物。但区别在于,如果if-guard不满意,comp-one将抛出异常。它也返回一个选项,使用哪个我想用我需要调用get方法(我不想这样做);

val items = for { 
    user <- findUser(userId) if user.isDefined 
    address <- findAddress(user.addressId) if address.isDefined 
    city <- findCity(address.cityId) if address.isDefined 
} yield (user.get, address.get, city.get) 

再次可能会建议捕捉异常,但我从许多来源读取捕捉异常被认为是不好的。此外,该例外不会提供哪种情况说明不符合条件。

同样的事情也适用于return语句。由于我自java和基于.net的语言,我倾向于使用下面的样式。

val user = Await.result(findUser(userId), timeout) 
if (user.isEmpty) { 
    return Future.successful(NotFound("No user with given id")) 
} 

val address = Await.result(findAddress(user.get.addressId), timeout) 
if (address.isEmpty) { 
    return Future.successful(NotFound("No address with given id")) 
} 

val city = Await.result(findUser(address.get.cityId), timeout) 
if(city.isEmpty) { 
    return Future.successful(NotFound("No city with given id")) 
} 

Future.successful(Ok) 

这在我的理解中绝对是不成问题的。首先它会导致代码块阻塞,其次它又迫使我使用get值,并使用与抛出异常类似的返回块来缩短执行时间。

一直未能找到一个优雅的解决方案。我目前正在与嵌套的方式,这使得它难以阅读

感谢

回答

1

优雅的解决这个问题是使用适当的数据类型来包装不同失败的案例去。

我建议你看看

Cats ValidatedScalaz Validation

这些类型的收集操作结果和内涵撰写好,可能与期货

+0

感谢,我将期待您有共享的库(也是什么monad) – Deliganli

0

您应该使用.failed期货而非successful沟通异常条件:

sealed NotFoundErr 
class NoUser extends Exception("No user with given id") NotFoundErr 
class NoAddress extends Exception("No address with given id") NotFoundErr 
class NoCity extends Exception("No city with given id") NotFoundErr 

def getOrElse[T](ifNot: Exception)(what: => Future[Option[T]]) = what 
    .map(_.getOrElse(throw ifNot)) 

val items = for { 
    user <- getOrElse(new NoUser)(findUser(userId)) 
    address <- getOrElse(new NoAddress)(findAddress(user.addressId)) 
    city <- getOrElse(new NoCity)(findCity(address.cityId))  
} yield (user, address, city) 

items 
.map(_ => Ok) 
.recover { case e: Exception with NotFoundErr => NotFound(e.getMessage) } 

你可以使它看起来更炫有一个隐含的:

object RichFuture { 
    implicit class Pimped[T](val f: Future[Option[T]]) extends AnyVal { 
     def orElse(what: => T) = f.map(_.getOrElse(what)) 
    } 
} 

现在,您可以编写换理解,如:

for { 
    user <- findUser(userId) orElse throw(new NoUser) 
    address <- findAddress(user.addressId) orElse throw(new NoAddress) 
    city <- findCity(address.cityId) orElse throw(new NoCity) 
} yield (user, address, city)