在后续函数Play framework 2.6x中使用Future [JsValue]

时间:2017-09-15 04:04:05

标签: json scala playframework future jsvalue

我是玩框架(2.6.x)和Scala的新手。我有一个返回Future [JsValue]的函数。如何在后续函数中使用Future [JsValue]?

def getInfo(): Future[JsValue] ={}

以下函数将使用JsValue中的值来计算某些内容。

在中间某处我必须从json响应中提取一个值。 val currentWeight = (jsValue \ "weight").as[String].toDouble

def doubleAmounts(currentWeight: Double): Double = {
  currentWeight*2.0
}

这里处理未来的正确方法是什么?我应该使用map还是onComplete从json获取weight

我尝试了这个,但它只在我调用doubleAmounts()后才解决。

val weight = getInfo() map { response =>
  if (response.toString().length > 0) (response \ "weight").as[String])
  else throw new Exception("didn't get response")
}

1 个答案:

答案 0 :(得分:0)

问题是,一旦你开始在Futures讲话,你需要继续在Futures讲话,这个想法是同一个服务器将处理所有这些等待和上下文的变化,所以玩它自我可以处理你的承诺,在某些情况下,你会返回一个结果。

因此,不是在控制器中调用返回Any的函数,而是可以处理Future[Any]

val weight: Future[String] = getInfo() map { response =>
  if (response.toString().length > 0) (response \ "weight").as[String])
  else throw new Exception("didn't get response")
}

如果你有几个未来的电话,每个人都需要其他Future的结果,你可以使用 For comprehension 来避免地图地狱。例如,这是一个更复杂的例子:

 def processSync(databaseServer: DatabaseServer, databaseIdentity: DatabaseIdentity): Future[String] = {
    val info = for {
      catalogs <- databaseSyncService.getCatalogs(databaseServer.address, databaseServer.port, databaseIdentity.login, databaseIdentity.password)
      clubbers <- getClubbers(databaseServer.id)
      ignoredCatalogs <- ignoredCatalogService.get(databaseServer.id)
    } yield (catalogs, clubbers, ignoredCatalogs)
    val result = info.map{
      case(catalogs, clubbers, ignoredCatalogs) => {
        val currentCatalogs = (clubbers.map(clubber =>
          Seq(Some(clubber.mainSchema), clubber.archiveSchema, clubber.blacklistSchema, clubber.logsSchema).flatten
        ).flatten ++  ignoredCatalogs).toSet
        val serverCatalogs = catalogs.toSet
        if(currentCatalogs == serverCatalogs) {
          "synchronized"
        } else {
          "outOfSync"
        }
      }
    }.recover{
      case sqlE: SQLServerException =>{
        logger.error(s"Conection error with ${databaseServer.name}", sqlE)
        "connectionError"
      }
    }
    for{
      realResult <- result
      _ <- databaseServerRepo.updateSync(databaseServer.id, realResult)
    } yield realResult
  }

for中的每个值都是未来,但是下面我们可以访问它们的值,在for的末尾,你使用yield来标记需要返回的内容。

其他示例可能是此控制器调用(忽略shilouete部分,是一个身份验证库,只需将返回类型视为Future [Result]):

def sync(id: Int) = silhouette.SecuredAction.async { implicit request: SecuredRequest[DefaultEnv, AnyContent] =>
    val actions = for {
      (server, identity) <- databaseServerService.getWithIdentity(id)
      databaseCatalogs <- databaseSyncService.getCatalogs(server.address, server.port, identity.login, identity.password)
      ignoredCatalogs <- ignoredCatalogService.get(id)
      clubberIntegrations <- clubberIntegrationService.getClubbersListOrAutoFill(id, databaseCatalogs, ignoredCatalogs.map(_.name))
    } yield Future.successful {
      val catalogs = databaseCatalogs.map { x =>
        val ignoredCatalogNotes = ignoredCatalogs.filter(_.name == x).map(_.note).headOption
        DatabaseCatalog(x, ignoredCatalogNotes.getOrElse(""), ignoredCatalogNotes.isDefined)
      }
      Ok(web.databaseserver.views.html.databaseServerSync(request.identity, server, DatabaseServerForSync(clubberIntegrations, catalogs)))
    }
    actions.recover {
      case _: SQLServerException => {
        databaseServerService.getName(id).map(x => {
          Redirect(web.databaseserver.controllers.routes.DatabaseServerController.list())
            .flashing("error" -> Messages("databaseSync.couldNotConnect", x))
        })
      }
    }.flatMap(x => x)
  }

另外,我忘了提一下,如果你需要处理一个使用Future的函数而另一个函数没有,你可以使用Future.successful转换后者,还有Future.sequence来转换{ {1}}进入Seq[Future[Any]]和其他功能,以帮助您以不同的方式处理未来。