Подтвердить что ты не робот

Избавиться от Scala Будущее гнездование

Снова и снова я борюсь, когда функция опирается на некоторые будущие результаты. Это обычно сводится к результату, например Future [Seq [Future [MyObject]]]

Чтобы избавиться от этого, я теперь использую Await внутри вспомогательной функции, чтобы вывести объект, не являющийся будущим, и уменьшить вложенность.

Похоже на это

def findAll(page: Int, perPage: Int): Future[Seq[Idea]] = {
    val ideas: Future[Seq[Idea]] = collection.find(Json.obj())
    // [...]

    ideas.map(_.map { // UGLY?
      idea => {
        // THIS RETURNED A Future[JsObject] before
        val shortInfo: JsObject = UserDao.getShortInfo(idea.user_id)
        idea.copy(user_data = Some(shortInfo))
      }
    })
}

Этот код работает, но для меня он выглядит довольно взломанным. Два вызова карты - еще одна ошибка. Я потратил часы, пытаясь понять, как сохранить это полностью асинхронным и вернуть простой будущий Seq. Как это можно решить, используя лучшие методы Play2?

Edit Чтобы сделать usecase более понятным:

У меня есть объект A от mongodb (reactivemongo) и хочу добавить информацию, поступающую с другого вызова в mongodb getShortInfo. Это классический случай "получить пользователя для этого сообщения", который будет разрешен с присоединением к РСУБД. getShortInfo, естественно, создаст будущее из-за вызова в db. Чтобы уменьшить вложенность в findAll, я использовал Await(). Это хорошая идея?

findAll вызывается из асинхронного действия Play, преобразованного в Json и отправленного по проводу.

def getIdeas(page: Int, perPage: Int) = Action.async {

  for {
    count <- IdeaDao.count
    ideas <- IdeaDao.findAll(page, perPage)
  } yield {
    Ok(Json.toJson(ideas))
  }
}    

Поэтому я думаю, что возвращение Seq[Future[X]] из findAll не принесет лучшую производительность, так как я должен ждать результата в любом случае. Правильно ли это?

Краткая справка: Возьмите вызов Future, возвращающий последовательность, используйте каждый элемент результата для создания другого вызова Future, возвращайте результат к асинхронному действию таким образом, чтобы не возникало никаких ситуаций блокировки.

4b9b3361

Ответ 1

Две удобные функции на сопутствующем объекте Future, который вы должны знать, могут помочь здесь, первая, и проще обернуть голову вокруг. Future.sequence. Он занимает некоторое будущее и возвращает будущее последовательности. Если заканчивается с помощью Future[Seq[Future[MyObject]]], вызывается, что result. то вы можете изменить это на Future[Future[Seq[MyObject]]] с помощью result.map(Future.sequence(_))

Затем, чтобы свернуть a Future[Future[X]] для любого X, вы можете запустить "result.flatMap(identity)", на самом деле вы можете сделать это для любого M[M[X]] для создания M[X], пока M имеет flatMap.

Еще одна полезная функция здесь - Future.traverse. Это в основном результат принятия Seq[A], сопоставление его с Seq[Future[B]], а затем запуск Future.sequence для получения Future[Seq[B]] So в вашем примере:

ideas.map{ Future.traverse(_){ idea =>
    /*something that returns a Future[JsObject]*/
} }.flatMap(identity)

Однако, во многих случаях, когда вы выполняете flatMap (identity), вы можете превратить карту в flatMap, и это имеет место здесь:

ideas.flatMap{ Future.traverse(_) { idea =>
    /*something that returns a Future[JsOjbect]*/
} }

Ответ 2

документация Akka содержит хороший обзор того, как бороться с композициями фьючерсов. В общем, в нем описываются четыре метода в scala.concurrent.Future, которые могут быть использованы для уменьшения состава фьючерсов в одном экземпляре Future:

  • Future.sequence
  • Future.traverse
  • Future.fold
  • Future.reduce