Scala/Play: Create a Future[ List[ ... ] ] from a (partial)



我有一个东西列表,让我们说字符串。
我想把它转换成一个未来[列表[ ]]的东西,让我们再说字符串。
不需要并行执行。

这将最终出现在 Action.async 中,因此不欢迎阻止。

我有一个处理程序,将来可能会转换元素。
简化为:

def handle( input: String ): Future[ String ] =
{
  input match {
    case "X" => Future.failed( new Exception( "failed on "+input ) )
    case other => Future.successful( "handled "+other )
  }
}

最后,我想返回一个 Future[ 列表[ ... ] ],包括第一个失败的结果,然后停止。
可以简化为:

  def handleInOrder( inputs: List[ String ] ): Future[ List[ String ] ] = {
    val output = new ListBuffer[ String ]( )
    breakable { for( input <- inputs ){
      Await.ready( handle( input ), Duration.Inf ).value.get match {
        case Success( result ) => output += result;
        case Failure( reason ) => output += "Ex:"+reason; break
      }
    } }
    Future.successful( output.toList )
  }

处理事情的顺序很重要。

这按原样工作,但我真的很想摆脱">Await.ready"。

我希望我的问题很清楚,目前我只是无法解决这个问题。

一个呼唤

handleInOrder( List( "a", "b", "X", "c" )

应该返回

List(handled a, handled b, Ex:java.lang.Exception: failed on X)

您可以使用foldLeft按顺序执行Future

def handleInOrder(inputs: List[String]): Future[List[String]] = {
    inputs.foldLeft(Future.successful(ListBuffer.empty[String])) { case (acc, next) => 
        handle(next).recover { case t: Throwable => "Ex: " + t.getMessage}
            .flatMap(f => acc.map(_ += f))            
    }.map(_.toList)
}

这将recover handle所有失败的Future,将它们替换为包含异常消息的成功。如果你想让整个Future[List[String]]在一个错误的输入上失败,你可以从链中删除recover

我建议使用Either(或 scalaz /(处理成功或失败,并为意外故障保留Future.failure,就像许多人建议不要对"正常"控制流使用异常一样。然后,您的返回值可以具有合理的类型,而不是随机包含 String s 和 Throwable s 的List

import scalaz._, Scalaz._
import scala.concurrent.Future, scala.concurrent.ExecutionContext.Implicits.global //or your own execution context
case class MyError(input: String)
def handle( input: String ): Future[ MyError / String ] =
  input match {
    case "X" => MyError(input).left
    case other => Future.successful( ("handled "+other).right )
  }
def handleInOrder(inputs: List[String]): Future[(List[String], MyError) / List[String]] = 
  inputs.foldLeftM(List().right: (List[String], MyError) / List[String]) {
    (acc, input) => acc match {
      case -/(alreadyFailed) => Future.successful(-/(alreadyFailed))
      case /-(successfulSoFar) =>
        handle(input) map {
          case /-(alsoSuccessful) => /-(alsoSuccessful :: successfulSoFar)
          case -/(nowFailed) => -/((successfulSoFar, nowFailed))
        }
      }
    }

这将在第一次失败时停止:

def handleInOrder(inputs: List[String]): Future[List[String]] = {
  def run(inputs: List[String], output: ListBuffer[String]): Future[ListBuffer[String]] = inputs match {
    case h :: t => handle(h).flatMap{x => run(t, output += x)}.recover{ case t: Throwable => output += ("Ex: " + t.getMessage)}
    case _ => Future { output }
  }
  run(inputs, ListBuffer.empty[String]).map(_.toList)
}

最新更新