コンパニオンオブジェクトFutureの全メソッド網羅いきます。
シリーズ目次
はじめに
コンパニオンオブジェクト操作
インスタンス操作
おまけ
Futureにハマる
Futureの単独生成&並列実行
apply (※要ExecutionContext、別スレッドでの実行)
通常はapplyを明示的に呼ぶことはないでしょう。以下のように使用。
val f: Future[String] = Future {
"hello"
}
なお、ネットでよく見るサンプルも公式ドキュメントもfuture{...}で生成しているものばかりなのですが、Scala2.11からscala.concurrentのfutureメソッドはdeprecated扱いになっており、代わりにFutureを使うよう記述されています。
よって本記事ではfutre{ ... }は使用せずFuture{ ... }で統一しています。
Futureの単独生成(実行済みFuture)
実行済みFutureを作成するため並列実行は行われない。よってAwaitによる待ちは不要。
当然、valueで呼び出してもNoneではなく全てSomeである。
successful
scala.util.Successなvalueを生成。
val f: Future[Int] = Future.successful(100)
failed
scala.util.Failureなvalueを生成。
val f: Future[String] = Future.failed{ new Exception("error in future") }
fromTry
TryからFutureを作成する。
val f: Future[String] = Future.fromTry(Try{
if (...) "hello" else throw new Exception("error in f")
})
Futureの複数生成&複数の並列実行
traverse (※要ExecutionContext、別スレッドでの実行)
TraversableOnceなものからFutureを複数生成&実行。
5だけ意図的に遅らせているが、結果は終わった順ではなく、traverseで処理した順に返ってくる。
よってこの結果は1,2,3,4,5,6,7,8,9,10,11,12となる。
val f: Future[List[Int]] = Future.traverse((1 to 12).toList) { i =>
Future {
println(s"start: $i")
if (i == 5) Thread.sleep(5000) else Thread.sleep(1000)
println(s"end: $i")
i
}
}
f.onSuccess{ case result: List[Int] => println(result.mkString(",")) }
Await.ready(f, Duration.Inf)
4コアCPUのMacBookの環境で実行すると、4つずつ同時に処理されることが確認できる。
sequence (※要ExecutionContext、別スレッドでの実行)
traverseの簡易版で、引数にFutureのTraversableOnceを取る。
結果については上記と同様。
val futures: List[Future[Int]] = (1 to 12).toList.map { i =>
Future {
println(i)
Thread.sleep(1000)
i
}
}
val f: Future[List[Int]] = Future.sequence(futures)
f.onSuccess{ case result => println(result.mkString) }
Await.ready(f, Duration.Inf)
Futureの複数生成&複数並列実行&特定の条件を満たしたFutureをひとつ返す
firstCompletedOf (※要ExecutionContext、別スレッドでの実行)
並列実行するFutureの中で最初に終わったFutureをひとつ返す。
3で例外が出るが、4の方が先に終わるのでonSuccessコールバックのほうが呼ばれる。なお、3と4の処理を入れ替えれば、onFailureコールバックのほうが呼ばれる。
val futures: Seq[Future[Int]] = Seq(
Future{ Thread.sleep(3000); 1 },
Future{ Thread.sleep(2000); 2 },
Future{ Thread.sleep(1000); throw new Exception("error in 3") },
Future{ Thread.sleep(500); 4 })
val f = Future.firstCompletedOf(futures)
f.onSuccess{ case result: Int => println(result) }
f.onFailure{ case t => println(t.getMessage()) }
Await.ready(f, Duration.Inf)
find (※要ExecutionContext、別スレッドでの実行)
第一引数にFutureのTraversableOnce、第二引数にBooleanを返す関数を指定し、それを満たす最初のFutureが返る。
以下の例の場合は最初に処理が終わるのは4だが、条件は奇数のものなのでこの場合は3が返る。
なお、例外が起きるFutureがある場合の振る舞いはfirstCompletedOfと同じ。
val futures: Seq[Future[Int]] = Seq(
Future{ Thread.sleep(3000); 1 },
Future{ Thread.sleep(2000); 2 },
Future{ Thread.sleep(1000); 3 },
Future{ Thread.sleep(500); 4 })
val f = Future.find(futures){ _ % 2 == 1 }
f.onSuccess{ case result: Option[Int] => println(result.get) }
Await.ready(f, Duration.Inf)
Futureの複数生成&複数並列実行&全てのFutureの結果のまとめあげ
fold (※要ExecutionContext、別スレッドでの実行)
各Futureの結果を畳み込み演算する。
以下は初期値10から初めて、畳み込んだ結果、20が返ってくる。
なお、ひとつでも例外が起きるFutureがいるとonFailureコールバックが呼ばれる。
val futures: Seq[Future[Int]] = Seq(
Future{ Thread.sleep(3000); 1 },
Future{ Thread.sleep(2000); 2 },
Future{ Thread.sleep(1000); 3 },
Future{ Thread.sleep(500); 4 })
val f = Future.fold(futures)(10){ (total, v) => total + v }
f.onSuccess{ case result: Int => println(result) }
Await.ready(f, Duration.Inf)
reduce (※要ExecutionContext、別スレッドでの実行)
初期値なしのfold。0から始まるので結果は10となる。
なお、ひとつでも例外が起きるFutureがいるとonFailureコールバックが呼ばれる。
val futures: Seq[Future[Int]] = Seq(
Future{ Thread.sleep(3000); 1 },
Future{ Thread.sleep(2000); 2 },
Future{ Thread.sleep(1000); 3 },
Future{ Thread.sleep(500); 4 })
val f = Future.reduce(futures){ (total, v) => total + v }
f.onSuccess{ case result: Int => println(result) }
Await.ready(f, Duration.Inf)
応用:組み合わせ
10個のFutureを実行するf1とf2を作成し、それらの結果をfoldで足し合わせるfを定義。
全てが終わったら数字を足し合わせて結果を出力する。結果は220。
val f1: Future[List[Int]] = Future.traverse((1 to 10).toList) { i =>
Future { Thread.sleep(i * 100); i }
}
val f2: Future[List[Int]] = Future.traverse((11 to 20).toList) { i =>
Future { Thread.sleep(i * 100); i }
}
val f: Future[List[Int]] = Future.fold(List(f1, f2))(List(10)){ (total, v) => v ++: total }
f.onSuccess{ case result: List[Int] => println(result.sum) }
Await.ready(f, Duration.Inf)
メソッドコンプリート状況
基本編と生成編を終えてメソッドコンプリート率は現時点では以下となりました。
trait Awaitable: 5/5
trait Future: 3/16
object Future: 10/10