Fel*_*ipe 2 scala future playframework
在我的Play网络应用程序中,我val resultRack = Await.result(futureList, Duration.Inf)用来获取Future的结果.还有另一种更好的方法(使用最佳实践)从数据库中获取结果吗?如果我使用onComplete或onSuccess我的COntroller完成执行,结果val还没有.下面是我的Controller方法.一切正常,但我需要遵循Scala中的更多最佳实践.
编辑:我已经在使用Action.async其他方法.但在这一个我基本上不能使用因为either.fold.我想我需要map先验证方法的所有代码才能验证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
}
}
)
}
Run Code Online (Sandbox Code Playgroud)
使用flatMap和map的新方法.我的问题是我rackSeq在Controller中创建并填充seq .在gpuSeq我使用创建该对象未评价,因为它是从未来.我该怎么做才能评估这个未来gpeSeq?在我的结果我只能看到rackSeq,但列表gpuSeq总是空的.
此外,如果代码Util.toTime(at)抛出错误,我无法理解recover.正如我所理解的那样,我可以做到这一点....
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."))
}
}
Run Code Online (Sandbox Code Playgroud)
mar*_*ira 10
不要Await.result在Play控制器内部使用.这将阻止线程并杀死使用像Play这样的反应式框架的主要好处之一.取而代之的map或flatMap将Future要产生Result.例如,假设您有以下内容RackRepository:
class RackRepository {
def racks: Future[Seq[Rack]] = ???
}
Run Code Online (Sandbox Code Playgroud)
在您的控制器中,而不是:
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))
}
Run Code Online (Sandbox Code Playgroud)
你所做的是,你使用Action.async并映射你的未来以产生一个结果:
def list = Action.async {
rackRepository.racks.map { racks =>
Ok(Json.toJson(racks))
}
}
Run Code Online (Sandbox Code Playgroud)
如果您需要嵌套多个未来结果,请flatMap改用.
从你的第一个例子,你需要做的是了解之间的差异map和flatMap.这个看起来是一个好的开始:
我们来看一些例子:
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
}
}
Run Code Online (Sandbox Code Playgroud)
看到?不Await.然后我们有你的代码:
val fGpu: Future[Seq[GpuRow]] = gpuRepository.getByRack(r.id)
// val total = fGpu.map(_.map(_.produced).sum)
val resultGpu = Await.result(fGpu, Duration.Inf)
Run Code Online (Sandbox Code Playgroud)
为什么不flatMap和map我一起做的结合f5?换句话说,为什么要Await开启fGpu而不是map返回Future[Result]?
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")
}
Run Code Online (Sandbox Code Playgroud)
当然,你需要使用Action.async和flatMapfor f.
这里有一些关于您的代码的事情,当然还有关于您对未来的问题的事情:
不要混合使用的模型的控制器:一般来说控制器被一组方法(在控制器类中),该得到的请求,并返回一个结果(OK,REDIRECT等)。模型是类/对象/接口中的一组方法,这些方法获取一组参数,处理外部资源并将其结果返回给控制器。
方法是您的朋友:您可以将代码更多地划分为不同的方法。例如,您的方法名称为,addRack但其主体还包含一些处理,您可以将它们放在控制器或模型中的不同方法中;取决于他们属于哪里。
永不等待:这是有原因的,这是因为您正在占用线程并且在等待时间内不会将它们单独留下。这将导致您的应用程序在内存和CPU使用率方面效率低下。
Map是您的朋友:调用返回未来的方法时,请使用map。例如,您要调用此方法:
def hiFromFuture : Future[String] = Future{...}
hiFromFuture.map{
futureResult: String => //when Future is successful
}
Run Code Online (Sandbox Code Playgroud)
flatmap如果将来有多个连续的电话,也应该使用。例如,假定hiFromFuture2具有与相同的签名/主体hiFromFuture:
hiFromFuture.map{
futureResult: String => hiFromFuture2.map{ futureResult => }
}
Run Code Online (Sandbox Code Playgroud)
应写为:
hiFromFuture.flatMap{
futureResult: String => //when first future is successful
hiFromFuture2.map{
futureResult => //when second future is successful
}
}
Run Code Online (Sandbox Code Playgroud)
避免Future[Future[String]]; 并得到Future[String]。
对于失败的期货也要恢复:如果没有得到结果怎么办?您使用恢复。例如:
hiFromFuture.map{gotData => Ok("success")}.recover{case e: Exception => BadRequest}
请注意,您可以使用在恢复块中预期的任何异常。