任意长度的未来递归模式/未来链 [英] Future Recursion Patterns/Future Chaining of arbitrary length

查看:54
本文介绍了任意长度的未来递归模式/未来链的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很好奇递归构建将顺序运行的Akka期货链的最佳方法,如果将来的 doWork 调用失败,则未来应该是重试最多3次,如果链用尽重试次数,则链条将失败。假设所有 doWork 调用均通过返回的将来 futChain 调用,则该操作只能完成。

I'm curious about the best way to recursively build a chain of Akka futures which will run sequentially, if a doWork call in a future fails, the future should be retried up to 3 times, the chain should fail if it runs out of retry attempts. Assuming all doWork calls pass the returned future futChain should only complete.

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
  }
}




  1. 如何将结果作为集合?即在此示例中,每个 String doWork 函数返回(我需要以某种方式修改递归函数返回 Futrue [List [String]]

  2. 我应该使用恢复 recoverWith

  3. 可以调用 flatMap 链接这些调用

  4. 我应该考虑尾部递归和堆栈溢出吗?

  5. 我最好递归吗建立期货清单并减少它们?

  1. How can I get the results as a collection? i.e. in this example each String return from the doWork function (I need to somehow modify the recurse func to return a Futrue[List[String]]
  2. Should I use recover or recoverWith?
  3. Is it ok to call flatMap to chain these calls
  4. Should I make considerations about tail recursion & stack overflows?
  5. Would I be better to recursively build a list of futures and reduce them?


推荐答案

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

You can implement a retryable Future like this:

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
    }       
}

这不是针对尾递归优化的,但是如果您只打算重试几次,就不会出现堆栈溢出的情况(而且我想如果它在前几次失败了,无论如何都会继续失败)。

This isn't optimized for tail recursion, but if you only intend on retrying a few times, you won't get a stack overflow (and I imagine if it's failed the first few, it's going to keep failing, anyway).

然后我将单独进行链接。如果您要链接的函数数量有限,则每个函数都取决于前一个函数(出于某种原因,您希望汇总结果),您可以使用进行理解(语法糖对于 flatMap ):

Then I would do the chaining separately. If you have a finite number of functions to chain together, each depending on the previous (and for some reason you want to aggregate the results) you can use for comprehensions (syntactic sugar for 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 )并行进行: / p>

For an arbitrarily long chains, you can do them in parallel using Future.sequence (Futures in the Akka library):

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))) 

这些功能的实现对您的用例非常敏感。如果链中的任何期货失败,上述两个函数将返回失败的期货。有时您会想要这样做,而其他时候则不需要。如果您只想收集成功的期货,并丢弃失败的期货而又不使整个结果失败,则可以添加一个额外的步骤来恢复失败。

The implementation of these functions is very sensitive to your use case. The two above functions will return failed futures if any of the futures in the chain failed. Sometimes you'll want this, other times not. If you want to collect only the successful futures, and discard failed ones without failing the entire result, you can add an extra step to recover the failures.

此外,两者之间的区别 recover recoverWith 之间是它接受的 PartialFunction 的类型。 恢复用默认值替换失败的期货,而 recoverWith 则使用另一个 Future 。对于我的重试 recoverWith 更合适,因为我试图恢复失败的未来本身。

Additionally, the difference between recover and recoverWith is the type of PartialFunction it accepts. recover replaces failed futures with default values, while recoverWith does so using another Future. In the case of my retry, recoverWith is more appropriate because I'm trying to recover the failed Future with itself.

这篇关于任意长度的未来递归模式/未来链的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆