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

在控制器中使用Future的最佳实践(Play Scala)

须志新
2023-03-14

在我的Play web应用程序中,我使用的是val resultRack=wait。result(futureList,Duration.Inf)从未来获取结果。有没有其他更好的方法(使用最佳实践)从数据库中获得结果?如果我使用onCompleteonSuccess我的控制器完成执行,结果还不在val中。下面是我的控制器方法。一切正常,但我需要在Scala中遵循更多的最佳实践。

编辑:我已经在使用操作了。其他方法上的异步。但在这个例子中,我不能使用,基本上是因为。折叠。我想在验证json之前,我需要一个映射来包围该方法的所有代码。

  def addRack = Action(parse.json) { request =>
    val either = request.body.validate[Rack]
    either.fold(
      errors => BadRequest("invalid json Rack.\n"),
      rack => {
        val f: Future[Option[RackRow]] = rackRepository.getById(rack.id)
        val result = Await.result(f, Duration.Inf)
        result match {
          case Some(r) =>
            // If the Rack already exists we update the produced and currentTime properties
            val fGpu: Future[Seq[GpuRow]] = gpuRepository.getByRack(r.id)
            // val total = fGpu.map(_.map(_.produced).sum)
            val resultGpu = Await.result(fGpu, Duration.Inf)
            val total = resultGpu.map(_.produced).sum
            rackRepository.update(r.id, Some(total), Some(System.currentTimeMillis))
            Ok("Rack already exists! Updated produced and currentTime.\n")
          case None =>
            // If the Rack does not exist we create it.
            val rackRow = RackRow(rack.id, rack.produced, System.currentTimeMillis)
            rackRepository.insert(rackRow)
            Ok
        }
      }
    )
  }

使用平面地图和map的新方法。我的问题是,我正在创建和填充Controller内部的seqrackSeq。我用来创建此对象的gpuSeq没有计算,因为它来自Future。我应该如何计算此FuturegpuSeq?在我的结果中,我只能看到rackSeq,但是gpuSeq的列表总是空的。

此外,如果代码Util.to时间(at)抛出一个错误,我不能用恢复来捕捉这个错误。

  def getRacks(at: String) = Action.async { implicit request: Request[AnyContent] =>

    var rackSeq: Seq[Rack] = Seq.empty
    var gpuSeq: Seq[Gpu] = Seq.empty

    rackRepository.get(Util.toTime(at)).flatMap { resultRack: Seq[RackRow] =>
      resultRack.map { r: RackRow =>
        gpuRepository.getByRack(r.id).map { result: Seq[GpuRow] =>
          result.map { gpuRow: GpuRow =>
            gpuSeq = gpuSeq :+ Gpu(gpuRow.id, gpuRow.rackId, gpuRow.produced, Util.toDate(gpuRow.installedAt))
            println(gpuRow)
          }
        }
        val rack = Rack(r.id, r.produced, Util.toDate(r.currentHour), gpuSeq)
        rackSeq = rackSeq :+ rack
      }

      //      val result = Await.result(listGpu, Duration.Inf)
      //      result.foreach { gpuRow =>
      //        gpuSeq = gpuSeq :+ Gpu(gpuRow.id, gpuRow.rackId, gpuRow.produced, Util.toDate(gpuRow.installedAt))
      //      }
      Future.successful(Ok(Json.toJson(rackSeq)).as(JSON))
    }.recover {
      case pe: ParseException => BadRequest(Json.toJson("Error on parse String to time."))
      case e: Exception => BadRequest(Json.toJson("Error to get racks."))
      case _ => BadRequest(Json.toJson("Unknow error to get racks."))
    }
  }

共有3个答案

钱劲
2023-03-14

如果您的问题是如何管理json验证的错误情况,在成功路径将返回Future的上下文中,您可以简单地将Result包装在已经成功完成的Future中,即Future.successful(BadRequest(...))。如下所示

def addRack = Action(parse.json).async { request =>
  val either = request.body.validate[Rack]
  either.fold(
    errors => Future.successful(BadRequest("invalid json Rack.\n")),
    rack => {
      rackRepository.getById(rack.id).map {
        case Some(r) =>
          //...

          Ok("Rack already exists! Updated produced and currentTime.\n")
        case None =>
          //...

          Ok
      }
    }
  )
}

然后,当你有嵌套期货时,你应该像马可斯佩雷拉和戴夫·罗斯所说的那样进行平面映射

陆宏扬
2023-03-14

这里有几件事是关于你的代码的,当然还有你关于未来的问题:

>

方法是你的朋友:你可以把你的代码分成不同的方法。例如,您的方法名为addRack,但它的主体也包含一些处理,您可以将它们放在控制器或模型中的不同方法中;取决于它们属于哪里。

永远不要等待:这是有原因的,那就是你正在占用线程,并且在等待期间不会让它们单独存在。这将导致应用程序在内存和cpu使用方面效率低下。

Map是你的朋友:当你调用一个返回未来的方法时,使用Map。例如,您希望调用此方法:

def hiFromFuture:Future[String]=Future{…}

hiFromFuture.map{
  futureResult: String => //when Future is successful 
}

如果您有多个连续的未来通话,也应该使用flatmap。例如,假设hiFromFuture2hiFromFuture具有相同的签名/正文:

hiFromFuture.map{
  futureResult: String => hiFromFuture2.map{ futureResult => }

}
hiFromFuture.flatMap{
  futureResult: String => //when first future is successful 
    hiFromFuture2.map{ 
      futureResult => //when second future is successful 
    }
}

避免未来[Future[String];并获取Future[String]

对于失败的未来,恢复也是如此:如果你没有得到结果呢?你可以使用恢复。例如:

hiFromFuture。映射{gotData=

请注意,您可以使用恢复块中预期的任何异常。

冀永寿
2023-03-14

永远不要在Play控制器中使用wait.result。这将阻塞线程,并扼杀使用像Play这样的反应式框架的主要好处之一。相反,map平地图theFuture来生成Result。例如,假设您有以下RackRepository

class RackRepository {
  def racks: Future[Seq[Rack]] = ???
}

在控制器中,而不是执行以下操作:

def wrong = Action {
  val racks: Future[Seq[Rack]] = rackRepository.racks
  // This is wrong, don't do that
  val racksSeq = Await.result(racks, Duration.Inf)
  Ok(Json.toJson(racksSeq))
}

你所做的是,你使用ction.async映射你的未来来生成一个结果:

def list = Action.async {
  rackRepository.racks.map { racks =>
    Ok(Json.toJson(racks))
  }
}

如果需要嵌套多个未来结果,则使用平映射代替。

从第一个示例中,您需要做的是理解mapflatMap之间的区别。这看起来是个好的开始:

未来-地图vs平面地图

让我们来看一些例子:

val firstFuture: Future[String] = ??? // it does not mater where it comes from
val secondFuture: Future[String] = ??? // it does not mater where it comes from

val f1: Future[Int] = firstFuture.map(_.toInt)
val f2: Future[Future[String]] = firstFuture.map(secondFuture)
val f3: Future[String] = firstFuture.flatMap(secondFuture)

// Let's start to combine the future values
val f4: Future[Future[String]] = firstFuture.map { first =>
  secondFuture.map { second =>
    first + second // concatenate
  }
}

// But what if we want a Future[String] instead of a Future[Future[String]]?
// flatMap to the rescue!
val f5: Future[String] = firstFuture.flatMap { first =>
  secondFuture.map { second =>
    first + second // concatenate
  }
}

看见否等待。然后我们有你的代码:

val fGpu: Future[Seq[GpuRow]] = gpuRepository.getByRack(r.id)
// val total = fGpu.map(_.map(_.produced).sum)
val resultGpu = Await.result(fGpu, Duration.Inf)

为什么不把flatMapmap结合起来,就像我对f5所做的那样?换句话说,为什么要在fGpu上等待,而不是map返回未来的[结果]

gpuRepository.getByRack(r.id).map { gpuRows =>
  val total = gpuRows.map(_.produced).sum
  rackRepository.update(r.id, Some(total), Some(System.currentTimeMillis))
  Ok("Rack already exists! Updated produced and currentTime.\n")
}

当然,您需要使用操作。异步flatMap用于f

 类似资料:
  • 提交对映改动 一次提交要包括一个相关改动。例如,对于两个错误的修复应该进行两次不同的提交。精简的提交可以让其他的开发团队人员更简单地明白其改动的用义。如果其中一次提交的改动出现了问题,也可以方便地回滚到改动之前的状态。借助暂存功能来标记相关的改动文件,Git 可以为你打造出非常精准的提交。 频繁地提交改动 经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。此

  • 问题内容: 我的应用程序中有一个基本工厂,可以处理API调用。目前,我正在使用以下形式: 在我的控制器中,我正在像这样处理诺言: 看来我可以将promise处理移至Factory,而不是在控制器中执行,但是我不确定这是否会带来比小型控制器更多的好处。 有人可以解释有关此模式的最佳做法吗? 问题答案: 最终由您自己决定要向服务调用者提供多少数据。如果需要,您可以肯定地将HTTP响应对象返回给调用者,

  • 问题内容: 这是一个现实的Angular问题,我无法解决。我喜欢Angular,但是这个问题现在困扰着我很多。 扩展现有控制器功能,并在应用程序的另一页上使用扩展控制器的最佳实践是什么?换句话说: 如何在Angular中继承控制器? 编辑 -2014年9月23日,不要以为我的原始用例的描述会帮助访问者更好地理解我在这里的用意。我认为这使人们偏离了真正的问题。 问题答案: 半年后,我想我完全了解发生

  • 问题内容: 我开发Joomla网站/组件/模块和插件,并且每隔一段时间我都需要使用JavaScript来加载页面时触发事件的功能。在大多数情况下,这是使用函数完成的。 我的问题是: 这是在页面加载时触发JavaScript事件的最佳方法,还是有更好/更新的方法? 如果这是触发页面加载事件的唯一方法,那么确保多个事件可以由不同的脚本运行的最佳方法是什么? 问题答案: 可以,但是您可能已经注意到, 它

  • CRUD代表创建,读取,更新,删除。这是四种方法(如果区分查看一个记录和查看所有记录,则是五种方法)。在Rails中,处理CRUD的规范方法似乎包括七种方法。例如,使用 速记法为Order对象创建路由会生成以下七条路由: 这就是我困惑的根源。为 和 设置单独的操作/路由有什么意义?使用单独的操作查看页面和在数据库中创建记录有什么好处?我理解它是如何在Rails中实现的,例如: 在使用Rails之前

  • 问题内容: 我编写了Eclipse插件并将某些类作为API导出,同时希望限制对其他类的访问。 我遵循将这些类分成“ .internal”子包的常见Eclipse实践。 但是,我不能在这些类上使用“包”或默认级别的访问权限,因为我要导出的类需要使用其中的许多内容。 防止或阻止我的API用户出于自己的目的使用这些类的最佳实践是什么?有自动检查器吗? 我承认我在别无选择时曾涉嫌使用Eclipse的一些内