web-dev-qa-db-ja.com

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))
      }
    })
}

このコードは機能しますが、私にはかなりハッキーに見えます。 2つのマップ呼び出しは別の欠陥です。これを完全に非同期に保ち、単純な将来のSeqを返す方法を理解するために何時間も費やしました。 Play2のベストプラクティスを使用してこれをどのように解決できますか?

編集ユースケースをより明確にするには:

Mongodb(reactivemongo)のオブジェクトAがあり、mongodb getShortInfoへの別の呼び出しからの情報を追加したいと思います。これは、RDBMSの結合で解決される、古典的な「この投稿のユーザーを取得する」ケースです。 getShortInfoは、dbを呼び出すため、当然、Futureを生成します。 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))
  }
}    

したがって、findAllからSeq[Future[X]]を返すと、とにかく結果を待たなければならないため、パフォーマンスが向上しないと思います。これは正しいです?

使用例:シーケンスを返すFuture呼び出しを取得し、結果の各要素を使用して別のFuture呼び出しを作成し、ブロッキング状況が発生しないように結果を非同期アクションに返します。

22
DanielKhan

知っておくべきFutureコンパニオンオブジェクトの2つの便利な関数がここで役立ちます。最初の、頭を包み込むのが簡単なのは_Future.sequence_です。先物のシーケンスを取り、シーケンスの先物を返します。 _Future[Seq[Future[MyObject]]]_で終わる場合は、それをresultと呼びましょう。次に、これをresult.map(Future.sequence(_))で_Future[Future[Seq[MyObject]]]_に変更できます。

次に、任意のXの_Future[Future[X]]_を折りたたむには、「result.flatMap(identity)」を実行できます。実際、任意の_M[M[X]]_に対してこれを実行して、_M[X]_を作成できます。 MにはflatMapがあります。

ここでのもう1つの便利な関数は_Future.traverse_です。これは基本的に、_Seq[A]_を取得し、それを_Seq[Future[B]]_にマッピングし、Future.sequenceを実行して_Future[Seq[B]]_を取得した結果です。したがって、この例では、次のようになります。

_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]*/
} }
_
50
stew

Akkaのドキュメント には、先物の構成を処理する方法についての概要があります。一般に、 scala.concurrent.Future の4つのメソッドの概要を説明します。これらのメソッドを使用して、先物の構成を1つのFutureインスタンスに減らすことができます。

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