从Scala未来完成返回值



来自Java背景,我一直在尝试自学Scala一段时间。作为其中的一部分,我正在做一个小型宠物项目,该项目公开了一个HTTP端点,该端点根据owner保存车辆的registration number并返回状态。

为了提供更多的上下文,我使用Slick作为FRM,它异步执行DB操作并返回Future。根据这个Future的输出,我想将status变量设置为返回到客户端。

这里,是代码

def addVehicleOwner(vehicle: Vehicle): String = {
    var status = ""
    val addFuture = db.run((vehicles returning vehicles.map(_.id)) += vehicle)
    addFuture onComplete {
      case Success(id) => {
        BotLogger.info(LOGTAG, s"Vehicle registered at $id ")
        status = String.format("Registration number - '%s' mapped to owner '%s' successfully", vehicle.registration,
          vehicle.owner)
        println(s"status inside success $status") //--------- (1)
      }
      case Failure(e: SQLException) if e.getMessage.contains("SQLITE_CONSTRAINT") => {
        status = updateVehicleOwner(vehicle)
        BotLogger.info(LOGTAG, s"Updated owner='${vehicle.owner}' for '${vehicle.registration}'")
      }
      case Failure(e) => {
        BotLogger.error(LOGTAG, e)
        status = "Sorry, unable to add now!"
      }
    }
    exec(addFuture)
    println(s"Status=$status") //--------- (2)
    status
  }
  // Helper method for running a query in this example file:
  def exec[T](sqlFuture: Future[T]):T = Await.result(sqlFuture, 1 seconds)

这在Java中相当简单。使用Scala,我面临以下问题:

  • 期望值在(1)处打印,但(2)总是打印空字符串,方法返回的也是这样。有人能解释一下原因吗?
  • 我甚至尝试将var status标记为@volatile var status,它仍然计算为空字符串。

  • 我知道,以上不是功能性的做事方式,因为我正在静音状态。对于这种情况,写代码的干净方式是什么?

  • 几乎所有的例子,我可以找到描述如何映射Success的结果或处理Failure通过做println。我想做的不止这些。
  • 我可以参考的小项目有哪些好的参考资料?特别是,遵循TDD。

不依赖status在闭包内完成,您可以在Future[T]之上使用recover处理异常,并且总是返回您想要的结果。这是利用了Scala表达式的特性:

val addFuture = 
  db.run((vehicles returning vehicles.map(_.id)) += vehicle)
    .recover {
      case e: SQLException if e.getMessage.contains("SQLITE_CONSTRAINT") => {
        val status = updateVehicleOwner(vehicle)
        BotLogger.info(
          LOGTAG, 
          s"Updated owner='${vehicle.owner}' for '${vehicle.registration}'"
        )
        status
      }
      case e => {
        BotLogger.error(LOGTAG, e)
        val status = "Sorry, unable to add now!"
        status
      }
    }
val result: String = exec(addFuture)
println(s"Status = $result")
result

注意,Await.result不应该在任何生产环境中使用,因为它会同步阻塞Future,这与您实际想要的完全相反。如果您已经使用Future来委派工作,您希望它以异步方式完成。我假设你的exec方法只是为了测试的目的。

最新更新