简体   繁体   English

使用带有onComplete的Scala期货列表进行异步处理以进行异常处理

[英]asynchronous processing using list of Scala futures with onComplete for exception handling

I'm trying to make a large number of external service calls, each followed up with exception handling and conditional further processing. 我正在尝试进行大量的外部服务调用,每个调用都进行了异常处理和有条件的进一步处理。 I thought it would be easy to extend this nice ( Asynchronous IO in Scala with futures ) example using an .onComplete inside, but it appears that I don't understand something about scoping and/or Futures. 我认为使用内部的.onComplete扩展这个不错的示例( 在带有Scala的Future中使用Scala进行异步IO )将很容易,但是似乎我对范围和/或Future并不了解。 Can anyone point me in the right direction please? 谁能指出我正确的方向?

#!/bin/bash
scala -feature $0 $@
exit
!#

import scala.concurrent.{future, blocking, Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.util.{Success, Failure}
import scala.language.postfixOps

val keylist = List("key1", "key2")

val myFuts: List[Future[String]] = keylist.map {
  myid => future {
    // this line simulates an external call which returns a future (retrieval from S3)
    val myfut = future { Thread.sleep(1); "START " + myid}

    var mystr = "This should have been overwritten"
    myfut.onComplete {
      case Failure(ex) => {
        println (s"failed with error: $ex")
        mystr = "FAILED"
      }
      case Success(myval) => {
        mystr = s"SUCCESS $myid: $myval"
        println (mystr)
      }
    }
    mystr
  }
}

val futset: Future[List[String]] = Future.sequence(myFuts)
println (Await.result(futset, 10 seconds))

on my computer (Scala 2.10.4), this prints: 在我的计算机(Scala 2.10.4)上,将打印:

SUCCESS key2: START key2
SUCCESS key1: START key1
List(This should have been overwritten, This should have been overwritten)

I want (order unimportant): 我要(顺序不重要):

SUCCESS key2: START key2
SUCCESS key1: START key1
List(SUCCESS key2: START key2, SUCCESS key1: START key1)

I would avoid using onComplete and trying to use it to do side-effecting logic on a mutable variable. 我会避免使用onComplete并尝试使用它对可变变量进行副作用。 I would instead map the future and handle the fail case as returning a different value. 相反,我会映射将来,并在失败情况下返回不同的值。 Here's a slightly modified version of your code, using map on the Future (via a for comprehension) and then using recover to handle the failure case. 这是代码的略微修改版本,在Future上使用map (通过a进行理解),然后使用recover处理失败情况。 Hopefully this is what you were looking for: 希望这是您想要的:

val keylist = List("key1", "key2")

val myFuts: List[Future[String]] = keylist.map {myid => 

  // this line simulates an external call which returns a future (retrieval from S3)
  val myfut = future { Thread.sleep(1); "START " + myid}
  val result = for (myval <- myfut) yield {
    val res = s"SUCCESS $myid: $myval"
    println(res)
    res
  }
  result.recover{
    case ex => 
      println (s"failed with error: $ex")
      "FAILED"          
  }

}

val futset: Future[List[String]] = Future.sequence(myFuts)
println (Await.result(futset, 10 seconds))

On complete does not return a new future, it just allows you to do something when that future is completed. “完成”不会返回新的未来,它只允许您在该未来完成时做些事情。 So your first Future block is returning before the onComplete is executed, thus you are getting back the original value of the string. 因此,在执行onComplete之前,您的第一个Future块将返回,因此您将获取字符串的原始值。

What we can do is use a promise, to return another future, and that future is completed by the result of the first future. 我们能做的就是使用承诺,返回另一个未来,而这个未来是由第一个未来的结果完成的。

  val keylist = List("key1", "key2")

  val myFuts: List[Future[String]] = keylist.map {
    myid => {
      // this line simulates an external call which returns a future (retrieval from S3)
      val myfut = Future {
        Thread.sleep(1); "START " + myid
      }
      var mystr = "This should have been overwritten"
      val p = Promise[String]()
      myfut.onComplete {
        case Failure(ex) =>
          println(s"failed with error: $ex")
          mystr = "FAILED"
          p failure ex
        case Success(myval) =>
          mystr = s"SUCCESS $myid: $myval"
          println(mystr)
          p success myval
      }
      p.future
    }
  }

  val futset: Future[List[String]] = Future.sequence(myFuts)
  println(Await.result(futset, 10 seconds))

What would be super handy would be a mapAll method as I asked about here: Map a Future for both Success and Failure 正如我在这里询问的那样,超级方便的方法是mapAll方法: 为成功和失败都映射未来

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM