LoginSignup
6
1

More than 3 years have passed since last update.

[Scala]Await.resultからFutureに乗り換える

Last updated at Posted at 2019-10-27

乗り換えた経緯と説明

とりあえずDBからデータ取ってくる処理を全部 Await.result でやってたのですが、様々な理由でFutureに乗り換えることにしました。
Await.resultがダメな理由としてはこちらの方の記事が参考になります。

参考) Await.result は避けよう

ざっくり説明すると

  • Await.result : ずっと処理を待ち続けてスレッドが解放されない
  • Future : 処理が終わったら処理を再開する。それまでスレッドは解放される

スクリプトの実行が終わるまでずっとPCの前で待ち続けている状態だと思うとイメージしやすいかなと。

Await.resultのイメージ

スクリプトが終わるまでずっと画面見て待ってる。
他の作業ができない

使用スレッド数は 2

  1. スクリプト実行のプロセス
  2. スクリプト返ってくるまでずっと画面見てる人物

Futureのイメージ

スクリプト実行が終わってから処理をすればいいとわかってる。
他の事ができる。ラーメンも食べれる

使用スレッド数は 1

  1. スクリプト実行のプロセスのみ

てなことで、ラーメンが食べれるFutureに乗り換えていきましょう。

DBの取得処理をAwait.resultからFutureに変える

今のコードがこんな感じです。

def findUser(userId: Int): Try[Option[User]] = {
    val sql =
      sql"""
            SELECT id, name
            FROM   user
            WHERE  user_id = "#${userId}"
      """.as[User]

    Try(Await.result(db.run(sql), Duration.Inf))
  }

これをFutureにこんな感じで変えてみます。 db.run に変えただけですね。

  def findUser(userId:Int): Future[Option[User]] = {
    db.run(
      sql"""
            SELECT id, name
            FROM   user
            WHERE  user_id = "#${userId}"
      """.as[User]
    )
  }

これでFuture型で返ってくるようになります。他のやり方もあるかと思いますので一例程度に。

コードの使用側をFutureに変える

余談ですが、 Await.result だと処理が終了してから返ってきますので get してもOKです。
反対に、 Future は処理が終わってない状態のものが返ってくるので直接 get とかしてはダメです。
None.get になります。(これで1回実装してやらかした)

今のコードがこんな感じです。

  • Tryの中身をmatchで取得して、Optionの中身をgetで取得
  // 返り値 : Try[Option[User]]
  hogeRepository.findUser match {
    case Success(OptionUser) =>
      OptionUser.get
    case Failure(e) => // エラー処理
  }

こんな感じで書き換えました。

  • mapでFutureが返ってから以降の処理を実行
  • Optionの中身をgetで取得
  // 返り値 : Future[Option[User]]
  hogeRepository.findUser.map { maybeUser =>
    maybeUser.get
  }

Futureのエラー処理は recover で書けるみたいなので、最終的にはこんな感じです。

  // 返り値 : Future[Option[User]]
  val futureUser = hogeRepository.findUser.map { maybeUser =>
    maybeUser.get
  }.recover {
    // エラー処理
    case e => {
      println(e)
      System.exit(1)
    }
  }

呼び出し側も変えなきゃダメなので途中から移行を考えてる人はちょっと骨が折れるかもしれませんね。

これで大量のアクセスにも耐えれそうなFutureの恩恵を受けれるコードに書き換える事ができました!

余談

処理の流れの中で、 Seq[Future[String]] みたいになって Future[Seq[String]] にしたい!なんて時は sequence を使うといい感じに処理できます。

    val seqFutureStrings :Seq[Future[String]] = hogeRepository.findStrings
    // Futureの入れ子の順番を逆にする
    val futureSeqStrings :Future[Seq[String]] = Future.sequence(futureNames)

    futureSeqStrings.map { Strings =>
      Strings.mkString(",")
    }
6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1