【发布时间】: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