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

将阻塞代码转换为使用scala期货,scala,asynchronous,Scala,Asynchronous,我的旧代码如下所示,其中所有db调用都被阻塞 我需要帮助将此转换为使用期货 def getUserPoints(username: String): Option[Long] db.getUserPoints(username) match { case Some(userPoints) => Some(userPoints.total) case None => { if (db.getSomething("abc")

我的旧代码如下所示,其中所有db调用都被阻塞

我需要帮助将此转换为使用期货

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)
            }
        }       
    }
}
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低于我返回的期货

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)
            }
        }       
    }
}
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(使用futures)

我尝试使用for compr,但开始出现wierd错误,比如Future[什么都没有]

var userPointsFut: Future[Long] = for {
  userPointsOpt <- db.getUserPoints(username)
  userPoints <- userPointsOpt
} yield userPoints.total
var userpoints out:Future[Long]=for{

userPointsOpt我认为这种设计的第一个问题是,对
未来的
阻塞调用的端口不应包装选项类型:

阻塞调用:
def-methingblocking(for:Id):选项[T]
应成为:
def-methingblocking(for:Id):未来[T]
而不是:
def-methingblocking(for:Id):未来[选项[T]]

阻塞调用给出一个值
Some(value)
None
,非阻塞未来版本给出一个
Success(value)
Failure(exception)
,以非阻塞方式完全保留
选项的语义

考虑到这一点,我们可以在
Future
上使用组合器对相关流程进行建模。让我们看看如何:

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

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")
然后,该过程将如下所示:

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)
    }
  }
}
现在我们可以重新编写我们的流程,保持与基于未来的合成相同的结构

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
}
type UserPoints=Long
对象数据库{
def getUserPoints(用户名:字符串):未来[选项[UserPoints]]=???
def getSomething(s:String):Future[Option[Long]]=???
def设定点(pointId:UserPoints,username:String):未来[单位]=???
def findPointsForUser(用户名:字符串):未来[选项[长]]=???
}
类pointsnotfind扩展异常(“bonk”)
类StuffNotFound扩展异常(“未找到sthing”)
def getUserPoints2(用户名:字符串):未来[选项[UserPoints]]={
val futureOpt=FutureO(db.getUserPoints(用户名))
.map(userPoints=>userPoints/*.total*/)
orElse{
(用于{

sthingElse我不能真正更改Future[Option[UserPoints]],我的库返回Future选项。@coolbreeze我添加了一种方法,在
Future[Option[t]]
上保留了相同的合成结构。我意识到我以前的回答中存在一个缺陷。如果您希望在返回值为
Future(None)时恢复
我们需要另一个combinator
orElse
,我已经介绍了它。相应地更新了其余的答案。你不应该在你的问题中发布答案。将其发布为“答案”。@talex我无法理解你评论的原因。想解释一下吗?