Scala FutureとPromiseについて

個人開発したアプリの宣伝
目的地が設定できる手帳のような使い心地のTODOアプリを公開しています。
Todo with Location

Todo with Location

  • Yoshiko Ichikawa
  • Productivity
  • Free

スポンサードリンク

Futureは後から結果がわかるといったコンテキスト。スレッドをコンテキストの懸念に抽象化することによって、関数型プログラミングに沿ったコンテキストとしてスレッドを扱うことができる(説明が合ってるかは謎...)。

Futureを使ってみる

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

val s = "message"
val f: Future[String] = Future {
  Thread.sleep(1000)
  s
}  //初期化時点でFutureに定義した処理が実行される

// Future完了時の処理。Thread.sleep(1000)後にprintlnされる
// この処理もFutureスレッドで処理される
f.foreach { case s: String =>
  println(s)
}
f.isCompleted //完了済みかの確認


またFutureは失敗コンテキストも返せる。

val f2: Future[String] = Future {
  Thread.sleep(1000)
  throw new RuntimeException("失敗")
}
f2.failed.foreach { case e: Throwable =>
  println(e.getMessage)
}


Futureの完了を同期的に待つ

呼び出し元スレッドでFutureの完了を待つ場合は、Await.readyメソッドを使用する。

import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.language.postfixOps

val s = "message"
val f: Future[String] = Future {
  Thread.sleep(1000)
  s
}

val ret = Await.ready(f, 5000 millisecond) // Thread.sleep(1000)後、"message" が表示される
ret.foreach{ s => println(s)}

また、場合によっては、Await.ready以降の処理が先に処理されているように見受けられる時があるが、それはforeach内での処理はFutureスレッドでの処理になるため並列処理で実行される為である。


FutureのSuccess, Failureのパターンマッチ

//FailFutureは例外がthrowされるケースがある
val f: Future[Double] = FailFuture.map(i => doAction(i))

f onComplete {
  case Success(v) => println( v )
  case Failure(t) => println( t.getMessage )
}


for式での折りたたみ

Future完了時のforeachをfor式で折りたたむことができる。複数のFutureを式内で扱える。

val compositeFuture: Future[(Int, Int)] = for {
  first <- futureFirst
  second <- futureSecond
} yield (first, second)

compositeFuture onComplete {
  case Success((first, second)) => println("両方とも成功")
  case Failure(t) => println( t.getMessage ) //どちらかまたは両方が失敗
}


Promise

Promiseは成功、失敗の振る舞いを与えることによって新たなFutureを返す。新たなFutureは作成時に与えられた値を元に完了時の処理を定義できる。

Futureが後から結果を取得できるという型だとしたらPromiseは後から結果を与える型として解釈(合ってるかどうかは分からない...)

import scala.concurrent._
import ExecutionContext.Implicits.global

val promise = Promise[Int]
val f = promise.future
f.foreach(e => println("promiseの成功結果"))

Future {
  val ret:Boolean = doAction()
  if(ret)
    promise.success(10)
  else {
    promise.failure(0)
  }
}
val result = Await.result(f, Duration.Inf)