首页 > 解决方案 > 如何使用包装了 Either 的 Future?

问题描述

在我当前的项目中,我使用Either[Result, HandbookModule]( Resultis an HTTP Statuscode) 作为返回类型,以便在出现问题时创建正确的状态。我现在已将我的数据库访问重构为非阻塞。

此更改要求我将数据库访问函数的返回类型更改为Future[Either[Result, HandbookModule]].

现在我不确定如何将此函数与另一个返回的函数粘合在一起Either[Result, Long]

为了更好地说明我的意思:

def moduleDao.getHandbooks(offset, limit): Future[Either[Result, List[Module]] = Future(Right(List(Module(1))))

def nextOffset(offset, limit, results): Either[_, Long] = Right(1)

def getHandbooks(
  offset: Long,
  limit: Long): Future[Either[Result, (List[HandbookModule], Long)]] = {
  for {
    results <- moduleDao.getHandbooks(offset, limit)
    offset  <- nextOffset(offset, limit, results)
  } yield (results, offset)
}

在更改之前,这显然没有问题,但我不知道最好的方法是什么。

或者有没有办法将 a 转换为Future[Either[A, B]]a Either[A, Future[B]]

标签: scalafunctional-programmingfutureeither

解决方案


为了从 Future 中解开您的方法,您必须阻止它并等待结果。您可以使用Await.result.

但是阻止未来通常不被认为是最佳实践。更多关于这个here

所以你应该以不同的方式解决这个问题。您面临的实际上是嵌套 monad 堆栈的常见问题,并且可以使用 monad 转换器处理。

Scala 的函数式编程库cat提供了EitherT monad 转换器的实现。

在您的情况下,您可以使用EitherT.applyto transform Future[Either[Result, List[Module]]intoEitherT[Future, Result, List[Module]]EitherT.fromEitherto lift Either[_, Long]

它可能看起来像这样:

import cats.data.EitherT
import cats.implicits._
  
def getHandbooks(
   offset: Long,
   limit: Long
): Future[Either[String, (List[String], Long)]] = {
  val result: EitherT[Future, String, (List[String], Long)] = for {
    results <- EitherT(moduleDao.getHandbooks(offset, limit))
    offset  <- EitherT.fromEither[Future](nextOffset(offset, limit, results))
  } yield (results, offset)

  result.value //unwrap result from EitherT
}

推荐阅读