LoginSignup
3
1

More than 5 years have passed since last update.

Scala の Future を直列で実行する

Posted at

はじめに

Scala で複数の Future を扱う際、皆様はどのように実装していますか?
Scala には複数の Future を1つにまとめる Future.sequence という便利なメソッドがあります。
ただし、 Future.sequence を使う場合、 Future は並列に実行されます。
ほとんどのケースで並列に実行されても問題ない(パフォーマンスとしても望ましい)のですが、直列に実行したいケースがあります。
具体的には、複数の API コールを行う際に同時接続数が制限されている場合です。
Future は作成された時点で実行されてしまうため、直列に実行するには少し工夫が必要になります。

問題

以下のような API のモックがあります。
引数を10倍し、 100ms 待って返すだけのものです。
この API を10回コールした際の動作を検証します。
(検証用コードなのでこのようになっていますが、本来は Thread.sleepFuture に使ってはいけません!)

def someApiCall(foo: Int): Future[Int] = {
  Future {
    println(s"start $foo")
    Thread.sleep(100)
    println(s"end $foo")
    foo * 10
  }
}

並列に実行

Await.result({
  Future.sequence({
    (1 to 10).map(someApiCall)
  })
    .map(println)
}, 100.seconds)
実行結果
start 2
start 8
start 3
start 7
start 4
start 1
start 5
start 6
end 7
end 2
end 6
end 3
end 4
end 8
end 1
end 5
start 9
start 10
end 10
end 9
Vector(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)

上のように、並列に実行すると開始・終了はバラバラになります。

直列に実行

Await.result({
  (1 to 10).foldLeft[Future[Seq[Int]]](Future.successful(Nil))({ (results$, i) =>
    for {
      results <- results$
      result <- someApiCall(i)
    } yield results :+ result
  })
    .map(println)
}, 100.seconds)
実行結果
start 1
end 1
start 2
end 2
start 3
end 3
start 4
end 4
start 5
end 5
start 6
end 6
start 7
end 7
start 8
end 8
start 9
end 9
start 10
end 10
List(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)

前の API コールが終了してから次が呼ばれています。

おわりに

あまりきれいとは言えないコードなので、改善案がございましたらご指摘くださると幸いです。

3
1
2

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
3
1