Использование Future[JsValue] в последующей функции Play framework 2.6x

Я новичок в фреймворке (2.6.x) и Scala. У меня есть функция, которая возвращает будущее [JsValue]. Как я могу использовать Future[JsValue] в последующей функции?

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

Следующая функция будет использовать значение из JsValue для вычисления чего-либо.

Где-то посередине я должен извлечь значение из ответа json. val currentWeight = (jsValue \ "weight").as[String].toDouble

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

Как правильно обращаться с будущим здесь? Должен ли я использовать карту или onComplete, чтобы получить weight от JSON?

Я пытался это, но это решает только после того, как я уже позвонил doubleAmounts(),

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

1 ответ

Проблема в том, что когда вы начинаете говорить в 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, вы можете использовать Для понимания, чтобы избежать ада карты. Например, вот немного более сложный пример:

 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 преобразовать Seq[Future[Any]] в Future[Seq[Any]] и другие функции, которые помогут вам управлять фьючерсами по-разному.

Другие вопросы по тегам