ScalaのFutureの実行タイミングを調べる
Scala3実装。
コードはほぼClaudeくんに書いてもらった。
code:scala
import java.lang.System
import scala.concurrent.duration.*
import scala.concurrent.{Await, ExecutionContext, Future}
given ExecutionContext = ExecutionContext.global
@main
def futureComparison(): Unit = {
// 指定ミリ秒待機する関数
def delay(ms: Int, value: String): FutureString = Future: Thread.sleep(ms)
println(s"$value 完了")
value
// 実行時間測定ヘルパー
def timeT(block: => T): T = val start = System.currentTimeMillis()
val result = block
val end = System.currentTimeMillis()
println(s"実行時間: ${end - start}ms\n")
result
println("=== 逐次実行(for式内で直接呼び出し) ===")
val sequential = time {
val result = for
a <- delay(1000, "タスクA") // 1秒後に完了
b <- delay(1000, "タスクB") // さらに1秒後に完了
c <- delay(1000, "タスクC") // さらに1秒後に完了
yield s"$a, $b, $c"
Await.result(result, 5.seconds)
}
println(s"結果: $sequential")
println("=== 並行実行(事前にval定義) ===")
val parallel = time {
val futureA = delay(1000, "タスクX") // 即座に開始
val futureB = delay(1000, "タスクY") // 即座に開始
val futureC = delay(1000, "タスクZ") // 即座に開始
val result = for
a <- futureA // 既に実行中のFutureを待機
b <- futureB // 既に実行中のFutureを待機
c <- futureC // 既に実行中のFutureを待機
yield s"$a, $b, $c"
Await.result(result, 5.seconds)
}
println(s"結果: $parallel")
}
結果
code:result
=== 逐次実行(for式内で直接呼び出し) ===
タスクA 完了
タスクB 完了
タスクC 完了
実行時間: 3029ms
結果: タスクA, タスクB, タスクC
=== 並行実行(事前にval定義) ===
タスクY 完了
タスクX 完了
タスクZ 完了
実行時間: 1008ms
結果: タスクX, タスクY, タスクZ
聞いていた通り、valで先に定義していた方はほぼ1秒で完了しているのに対して、for内でfutureを作ったものに関してはきっちり3秒かかっている。これにより
Futureは作成されたタイミングから処理が開始されること
forがflatmap, mapの糖衣構文であること
がわかる。脱糖するとこんな感じ。
code:desugaring
futureA.flatMap { a =>
futureB.flatMap { b =>
futureC.map { c =>
result
}
}
なので、futureAの処理結果が見えないと、futureBがキックされないのである。
依存関係がないFutureに関しては、先にvalで定義しておくことが大事。