首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将阻塞代码转换为使用scala期货

将阻塞代码转换为使用scala期货
EN

Stack Overflow用户
提问于 2016-10-21 01:38:55
回答 1查看 271关注 0票数 1

我的旧代码看起来像下面这样,所有的db调用都是阻塞的。

我需要帮助将此转换为使用Futures。

代码语言:javascript
复制
def getUserPoints(username: String): Option[Long]
    db.getUserPoints(username) match {
        case Some(userPoints) => Some(userPoints.total)
        case None => {
            if (db.getSomething("abc").isEmpty) {
                db.somethingElse("asdf") match {
                    case Some(pointId) => {
                        db.setPoints(pointId, username)
                        db.findPointsForUser(username)
                    }
                    case _ => None
                }
            } else {
                db.findPointsForUser(username)
            }
        }       
    }
}

我的新API位于我返回Futures的地方。

代码语言:javascript
复制
db.getUserPoints(username: String): Future[Option[UserPoints]]
db.getSomething(s: String): Future[Option[Long]]
db.setPoints(pointId, username): Future[Unit]
db.findPointsForUser(username): Future[Option[Long]]

我怎样才能将上面的代码转换为使用我的新API呢?

我尝试使用for-compr,但开始遇到像FutureNothing这样奇怪的错误。

代码语言:javascript
复制
var userPointsFut: Future[Long] = for {
  userPointsOpt <- db.getUserPoints(username)
  userPoints <- userPointsOpt
} yield userPoints.total

但所有的分支和if子句以及试图将其转换为期货,都会变得有点棘手。

EN

回答 1

Stack Overflow用户

发布于 2016-10-21 02:24:36

我认为这种设计的第一个问题是,对Future的阻塞调用的端口不应该包装Option类型:

阻塞调用:def giveMeSomethingBlocking(for:Id): Option[T]应该变成:def giveMeSomethingBlocking(for:Id): Future[T],而不是:def giveMeSomethingBlocking(for:Id): Future[Option[T]]

阻塞调用提供了一个值Some(value)None,非阻塞未来版本提供了一个Success(value)Failure(exception),它以非阻塞方式完全保留了Option语义。

考虑到这一点,我们可以在Future上使用组合子对所讨论的过程进行建模。让我们看看如何:

首先,让我们将API重构为我们可以使用的东西:

代码语言:javascript
复制
type UserPoints = Long
object db {
  def getUserPoints(username: String): Future[UserPoints] = ???
  def getSomething(s: String): Future[UserPoints] = ???
  def setPoints(pointId:UserPoints, username: String): Future[Unit] = ???
  def findPointsForUser(username: String): Future[UserPoints] = ???
}
class PointsNotFound extends Exception("bonk")
class StuffNotFound extends Exception("sthing not found")

然后,该过程将如下所示:

代码语言:javascript
复制
def getUserPoints(username:String): Future[UserPoints] = {
  db.getUserPoints(username)
  .map(userPoints => userPoints /*.total*/)
  .recoverWith{ 
    case ex:PointsNotFound => 
    (for {
      sthingElse <- db.getSomething("abc")
      _ <- db.setPoints(sthingElse, username)
      points <- db.findPointsForUser(username)
    } yield (points))
    .recoverWith{
      case ex: StuffNotFound => db.findPointsForUser(username)
    }
  }
}

哪种类型检查正确。

编辑

由于API是一成不变的,处理嵌套的一元类型的一种方法是定义一个MonadTransformer。简而言之,让我们让Future[Option[T]]成为一个新的单子,让我们称之为FutureO,它可以与同类产品组合在一起。1

代码语言:javascript
复制
case class FutureO[+A](future: Future[Option[A]]) {
  def flatMap[B](f: A => FutureO[B])(implicit ec: ExecutionContext): FutureO[B] = {
    val newFuture = future.flatMap{
      case Some(a) => f(a).future
      case None => Future.successful(None)
    }
    FutureO(newFuture)
  }

  def map[B](f: A => B)(implicit ec: ExecutionContext): FutureO[B] = {
    FutureO(future.map(option => option map f))
  }
  def recoverWith[U >: A](pf: PartialFunction[Throwable, FutureO[U]])(implicit executor: ExecutionContext): FutureO[U] = {
    val futOtoFut: FutureO[U] => Future[Option[U]] = _.future
    FutureO(future.recoverWith(pf andThen futOtoFut))
  }

  def orElse[U >: A](other: => FutureO[U])(implicit executor: ExecutionContext): FutureO[U] = {
      FutureO(future.flatMap{
        case None => other.future
        case _ => this.future
      }) 
    }
  }

现在我们可以重写我们的流程,保留与基于未来的组合相同的结构。

代码语言:javascript
复制
type UserPoints = Long 
object db { 
  def getUserPoints(username: String): Future[Option[UserPoints]] = ???
  def getSomething(s: String): Future[Option[Long]] = ???
  def setPoints(pointId: UserPoints, username:String): Future[Unit] = ???
  def findPointsForUser(username: String): Future[Option[Long]] = ???
}
class PointsNotFound extends Exception("bonk")
class StuffNotFound extends Exception("sthing not found")

def getUserPoints2(username:String): Future[Option[UserPoints]] = {
  val futureOpt = FutureO(db.getUserPoints(username))
  .map(userPoints => userPoints /*.total*/)
  .orElse{ 
    (for {
      sthingElse <- FutureO(db.getSomething("abc"))
      _ <- FutureO(db.setPoints(sthingElse, username).map(_ => Some(())))
      points <- FutureO(db.findPointsForUser(username))
    } yield (points))
    .orElse{
      FutureO(db.findPointsForUser(username))
    }
  }
  futureOpt.future
}

1,并向http://loicdescotte.github.io/posts/scala-compose-option-future/致谢

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40161024

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档