【问题标题】:Is it possible to convert synchronous function call with output of Either[Throwable, Unit] to Future[Either[Throwable, Unit]]?是否可以将带有 Either[Throwable, Unit] 输出的同步函数调用转换为 Future[Either[Throwable, Unit]]?
【发布时间】:2019-12-28 12:32:02
【问题描述】:

我有这个生成 Either[Throwable, Unit] 的 scala 函数,其中有一个 retry 调用。 someClient.insertDetails 函数返回一个实体;不是 Future 或任何东西,只是案例类的一个实例。

  def insertDetails(phone: String, id: ID): Either[Throwable, Unit] = {

    withRetry(3) {
      someClient.insertDetails(phone, id)
    }.toEither
      .flatMap(res => )
      .leftMap(t => new Throwable(t.toString))
  }

重试实现如下所示。

  @scala.annotation.tailrec
  private def withRetry[T](retries: Int)(f: => T): Try[T] = {
    Try(f) match {
      case x: Success[T] => x
      case _ if retries > 1 => withRetry(retries - 1)(f)
      case f => f
    }
  }

现在,我在流管道中使用insertDetails。管道中调用insertDetails 的函数如下所示。

  def createAccount(createAcc: CreateAcc): Future[Either[Throwable, Account]] = createAcc match {
    case CreateAcc(phone, id) => (for {
      acc <- EitherT(repo.get(id).map(_.leftMap(error => CustomError(errorMessage = Some(error.toString)))))
      something <- EitherT.fromEither[Future](someOtherClient.createSomething(acc, id)).map { something: Something =>
        val _ = client.insertDetails(phone, something.id) match {
          case Left(t) =>
            sendToNotification(t.getMessage)
          case _ =>
        }

        Account(something.Result, id)
      }
    } yield acc).leftMap(error => new Throwable(error.toString)).value
  }

我注意到,通过在createAccount 中添加insertDetails,它会在等待insertDetails 完成时为createAccount 的执行添加“延迟”。问题是,我不需要使用来自insertDetails 的响应,只希望它在Exception 的情况下发送通知。所以,我正在考虑让insertDetails 返回Future[Either[Throwable, Unit]] 而不是Either[Throwable, Unit],所以这就像是“一劳永逸”。

我怎样才能做到这一点?

【问题讨论】:

  • 为什么不只是Future[Unit]? Futures 原生支持故障处理。
  • 当然你可以简单地将执行包装在FutureFuture { client.insertDetails(phone, something.id) match {.....} }
  • 1565986223 有正确的想法,imo。只需将该代码块包装在 Future 块中,整个事情就会在由执行上下文的调度程序确定的另一个线程上运行。然后,您无需等待 insertDetails 和 sentToNotification 完成即可继续执行其他代码。
  • 这算是一种好习惯吗?还有另一种被认为是“优雅”的方式吗?
  • @thecruisy 我很难想到一些特别优雅的方式来做你所描述的事情,因为 sendToNotification 从根本上来说是副作用。将代码块包装在 Future 中至少解决了如何使用并行性来消除代码中的瓶颈的问题。这很简单,即使它不像 monad 转换器那样华而不实,如果我处于您的情况,我宁愿避免出现在您的代码中。这完全是我个人的意见。

标签: scala monads future scala-cats either


【解决方案1】:
import zio._
import zio.blocking._

type ID = Int

val someClient = new {
  def insertDetails(phone: String, id: ID): Either[Throwable, Unit] =
    id match {
      case 321 => Right(())
      case _ => Left(new Exception())
    }
}

def insertDetails(phone: String, id: ID) =
  blocking(ZIO.fromEither(someClient.insertDetails(phone, id)))
    .retry(Schedule.recurs(3))

val runtime = new DefaultRuntime {}

runtime.unsafeRun(insertDetails("321098", 321))
//()
runtime.unsafeRun(insertDetails("123456", 123).either)
//res1: Either[Any,Unit] = Left(java.lang.Exception)
runtime.unsafeRun(insertDetails("123456", 123))
//Fiber failed.
//A checked error was not handled.
//java.lang.Exception
//at $line4.$read$$iw$$iw$$iw$$iw$$iw$$iw$$anon$1.insertDetails(<console>:5)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-19
    • 1970-01-01
    • 2016-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多