当前位置: 首页 > 知识库问答 >
问题:

Scala:使用异常处理来组合期货结果

戚鸿福
2023-03-14

我是Scala未来的新手,我还没有找到问题的解决方案。我正在努力实现以下目标(总体描述:努力获取一个酒店列表的客人列表,分别查询每个酒店):

    < li >对另一个API进行n次调用,每次调用都超时 < li >合并所有结果(将列表转换为包含所有元素的列表) < li >如果单个调用失败,记录错误并返回一个空列表(本质上,在这种情况下,如果我得到部分结果总比没有结果好) < li >理想情况下,如果单个调用失败,在等待一段时间后重试x次,最终失败并处理错误,就像没有重试一样

这是我的代码。< code>HotelReservation表示我将调用的外部API。

import com.typesafe.scalalogging.slf4j.Logging

import scala.concurrent._, ExecutionContext.Implicits.global
import scala.util.{Failure, Success}

case class Guest(name: String, country: String)

trait HotelReservation extends Logging {

  def getGuests(id: Int): Future[List[Guest]] = Future {
    logger.debug(s"getting guests for id $id")
    id match {
      case 1 => List(new Guest("John", "canada"))
      case 2 => List(new Guest("Harry", "uk"), new Guest("Peter", "canada"))
      case 3 => {
        Thread.sleep(4000)
        List(new Guest("Harry", "austriala"))
      }
      case _ => throw new IllegalArgumentException("unknown hotel id")
    }
  }
}

object HotelReservationImpl extends HotelReservation

< code>HotelSystem拨打电话。

import com.typesafe.scalalogging.slf4j.Logging

import scala.util.control.NonFatal
import scala.util.{Failure, Success}
import scala.concurrent._, duration._, ExecutionContext.Implicits.global

class HotelSystem(hres: HotelReservation) {

  def pollGuests(hotelIds: List[Int]): Future[List[Guest]] = {

    Future.sequence(
  hotelIds.map { id => future {
    try {
      Await.result(hres.getGuests(id), 3 seconds)
    } catch {
      case _: Exception =>
        Console.println(s"failed for id $id")
        List.empty[Guest]
    }

  }
  }
).map(_.fold(List())(_ ++ _)) /*recover { case NonFatal(e) =>
  Console.println(s"failed:", e)
  List.empty[Guest]
}*/
  }
}

还有测试

object HotelSystemTest extends App {

  Console.println("*** hotel test start ***")

  val hres = HotelReservationImpl

  val hotel = new HotelSystem(hres)
  val result = hotel.pollGuests(List(1, 2, 3, 6))

  result onSuccess {
    case r => Console.println(s"success: $r")
  }

  val timeout = 5000
  Console.println(s"waiting for $timeout ms")
  Thread.sleep(timeout)
  Console.println("*** test end ***")
}

1和2正在工作。所以是3,但我想我在某个地方读到了,所以尝试捕捉未来的召唤不是一个好主意,最好使用recover。但是,在这种情况下,如果我使用recover,如果出现单个故障,整个调用将失败并返回一个空列表。关于如何改进这一点有什么想法吗?

共有1个答案

郗河
2023-03-14

事实上,有两件事你可以做得不同:把尝试抛在脑后,不要在期货交易中使用等待。

下面是实现< code>pollGuests的一种更好的方法:

Future.sequence(
   hotelIds.map { hotelId =>
      hres.getGuests(hotelId).recover {
         case e: Exception => List.empty[Guest]
      }
   }
).map(_.flatten)

这里的第一点是,您不必在 pollGuests() 中使用 Futures,因为 getGuests() 已经为您提供了一个 Future。您只需使用 recover() 创建一个新的 Future,以便在返回 Future 时已经处理了可能的故障

第二点是您不应该使用Aetc。它会让您的代码阻塞,直到Future准备好,例如可能会冻结您的整个UI线程。我假设您使用Aetc是为了能够使用try-catch,但由于Recovery(),您不再需要它了。

 类似资料:
  • 我正在使用: Scala 2.10 游戏2.1 目前,我正在使用 类,但我愿意尝试另一个 API。 我很难将多个期货的结果组合成一个列表[(String, String)]。 以下 方法成功地将单个 Future 的结果返回到 HTML 模板: 方法执行 Web 服务 API 调用并返回 Future[play.api.libs.ws.Response]。方法 向 HTML 模板返回 List[(

  • 主要内容:抛出异常,捕获异常,实例,finally 语句,实例Scala 的异常处理和其它语言比如 Java 类似。 Scala 的方法可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。 抛出异常 Scala 抛出异常的方法和 Java一样,使用 throw 方法,例如,抛出一个新的参数异常: 捕获异常 异常捕捉的机制与其他语言中一样,如果有异常发生,catch 字句是按次序捕捉的。因此,在 catch 字句中,越具体的异常越要靠前,越普遍的

  • Scala 的异常处理和其它语言比如 Java 类似。 Scala 的方法可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。 抛出异常 Scala 抛出异常的方法和 Java一样,使用 throw 方法,例如,抛出一个新的参数异常: throw new IllegalArgumentException 捕获异常 异常捕捉的机制与其他语言中一样,如果有异常发生,catch字句是按次序

  • 我正在阅读Scala 2.11.8留档的函数在scala.concurrent.Future模块,它说: 将副作用函数应用于这个未来的结果,并返回一个包含这个未来的结果的新的未来。 这个方法允许强制回调以指定的顺序执行。 请注意,如果其中一个链式第四个回调引发异常,则该异常不会传播到后续的第四个调用。相反,随后的第四次回调将被赋予此未来的原始值。 我不确定< code >和不传播异常到底是什么意思

  • 在用收益结构组合期货时,有些有副作用,有些没有,我引入了竞争条件,因为取决于副作用的未来并没有将副作用的结果作为论据。 简而言之: 未来b读取由来自未来a的副作用改变的值,但是未来a不明确依赖于未来b的结果,因此可能在b完成读取之前发生。 为了解决这个问题,我的同事引入了一个虚拟函数,将b的结果作为参数并简单地将其丢弃。这样做是为了使依赖显式化。 实际代码如下: 在这种情况下,未来 b 为 带哑参

  • 我有一个监督Akka演员,它使用路由器将消息转发给工人演员。 我有一个封装了管理程序的类,当我调用该类的方法时,它“要求”管理程序做一些事情,然后我使用< code > await . result(the future)等待结果(没有结果我无法继续)。 如果worker抛出异常,我想重新启动抛出异常的worker,我想让调用包装类的代码捕获异常。 我向路由器构造函数传递了一个< code>One