加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 未来递归模式/未来任意长度的链接

发布时间:2020-12-16 10:06:30 所属栏目:安全 来源:网络整理
导读:我很好奇递归建立一个将按顺序运行的Akka期货链的最佳方式,如果未来的doWork调用失败,未来应该重试3次,如果重试耗尽,链条应该会失败尝试.假设所有doWork调用都通过了返回的未来futChain应该只完成. object Main extends App { val futChain = recurse(2) def
我很好奇递归建立一个将按顺序运行的Akka期货链的最佳方式,如果未来的doWork调用失败,未来应该重试3次,如果重试耗尽,链条应该会失败尝试.假设所有doWork调用都通过了返回的未来futChain应该只完成.

object Main extends App {
  val futChain = recurse(2)

  def recurse(param: Int,retries: Int = 3): Future[String] {
    Future {
      doWorkThatMayFailReturningString(param...)
    } recoverWith {
      case e => 
        if (retries > 0) recurse(param,retries -1)
        else  Future.failed(e)
    } flatMap {
      strRes => recurse(nextParam) //how should the res from the previous fut be passed?
    }
  }

  futChain onComplete {
    case res => println(res) //should print all the strings
  }
}

>我如何将结果作为集合获得?即在本例中每个String从doWork函数返回(我需要以某种方式修改recurse函数以返回Futrue [List [String]]
>我应该使用recover还是recoverWith?
>调用flatMap链接这些调用是否可以
>我应该考虑尾递归&堆栈溢出?
>我会更好地递归建立一个期货清单并减少它们吗?

解决方法

您可以像这样实现可重试的Future:

def retry[T](f: => Future[T])(n: Int)(implicit e: ExecutionContext): Future[T] = {
    n match {
        case i if (i > 1) => f.recoverWith{ case t: Throwable => retry(f)(n - 1)}
        case _ => f
    }       
}

这不是针对尾递归进行优化的,但是如果你只打算重试几次,你就不会得到堆栈溢出(我想如果它在前几个失败了,它会继续失败,无论如何).

然后我会分开做链接.如果您有一定数量的函数链接在一起,每个函数都取决于之前的(并且由于某种原因,您希望聚合结果),您可以使用for comprehensions(flatMap的语法糖):

for {
    firstResult <- retry(Future(doWork(param)))(3)
    secondResult <- retry(Future(doWork(firstResult)))(3)
    thirdResult <- retry(Future(doWork(secondResult)))(3)
} yield List(firstResult,secondResult,thirdResult)

对于任意长链,您可以使用Future.sequence(Akka库中的Futures)并行执行:

def doWork(param: String): String = ...

val parameters: List[String] = List(...)

val results: Future[List[String]] = Future.sequence(parameters.map(doWork(_)))

这将解释List [Future [String]]到Future [List [String]]的原因.

这是按顺序执行类似操作的一种方法:

def sequential[A,B](seq: List[A])(f: A => Future[B])(implicit e: ExecutionContext): Future[List[B]] = {
    seq.foldLeft(Future.successful(List[B]())) { case (left,next) =>
        left.flatMap(list => f(next).map(_ :: list))
    }
}

def doWork(param: String): String = ...

val results: Future[List[String]] = sequential(parameters)(param => Future(doWork(param)))

这些函数的实现对您的用例非常敏感.如果链中的任何期货失败,上述两个函数将返回失败的期货.有时候你会想要这个,有时则没有.如果您只想收集成功的期货,并在不使整个结果失败的情况下丢弃失败的期货,您可以添加额外的步骤来恢复失败.

另外,recover和recoverWith之间的区别是它接受的PartialFunction的类型. recover使用默认值替换失败的future,而recoverWith使用另一个Future替换失败的future.在我重试的情况下,recoverWith更合适,因为我试图用自己恢复失败的Future.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读