Help us understand the problem. What is going on with this article?

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

乗り換えた経緯と説明

とりあえず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(",")
    }
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした