続き
前回 のJavaScirpt に続いて今回は Scala です。
以下を見ながらまとめました。
Scala の Future
scala.concurrent.Future から見ていきます。
概要
-
Futureの生成(処理内容の登録)は Future コンパニオンオブジェクトの apply に処理を渡せばよい。
- コードの見た目は
Future { ... }
になる。 - 小文字始まりの
future { ... }
はなくなる予定らしい
- コードの見た目は
-
Future を生成するとすぐに、implicit な ExecutionContext によってスレッドが生成され、実行開始される。もちろん自分で ExecutionContext を指定もできる
-
import scala.concurrent.ExecutionContext.Implicits.global
が必要
-
-
結果の取得は
f.value
。Success
かFailure
が入っている -
終了を待ちたいときは
Await.result(mytask, timeout)
を使う。例外を受け取らないAwait.ready
もある -
終了時コールバックは Future のインスタンスの onComplete に正常・エラーの両方の処理を渡す
Future[Int] mytask = future { heavyTask() } // 本当は重い処理を書く
mytask onComplete {
case Success(result) => // 成功時の処理
case Failure(ex) => // 失敗時の処理
}
-
onSuccess
、onFailure
を使ってコールバックを登録してもよい
mytask onSuccess {
case result => // 成功時の処理
}
mytask onFailure {
case ex => // 失敗時の処理。例外の型を指定するとマッチしたときだけ発火する
}
- コールバックを何度も登録したときの呼び出し順は保障されない
Future の合成
- scala らしく
map
で合成する。 -
flatMap
も使えるので for comprehention でも書ける - 失敗は
recover
または recoverWith`(あたらしいFutureを返す場合) で受け取れる
コード例
val f = Future { 5 }
val g = f.map { case x => x + 1 } foreach println
// 6 が表示される
val h = f.flatMap { case x => Future { x + 1 } } foreach println
// 6 が表示される
val f = for {
x <- Future { 5 }
y <- Future { x + 1 }
} yield y + 2
Await.result(f, Duration("10 millis"))
// 5, x+1, y+2 がそれぞれ終了次第順次実行して 8 が表示される
val f1 = Future { Thread.sleep(5000); 5 }
val f2 = Future { Thread.sleep(5000); 3 }
(for { x <- f1; y <- f2 } yield (x+y)) foreach println
// 2つの sleep を並列実行して、5秒後に 8 が表示される
// sbt console からだと上記3つを急いで実行しないとよくわからないことになります
// 先ほどの順次実行の例のようにfor の中に Future の生成を書いてしまうと、
// f1 が終わるまで f2 が始まらないので並列実行にならないことに注意
val h = f.flatMap {
case x => Future { x / 0 }
} recover {
case x => 10
} foreach println
// ゼロ除算でエラーになるので recover が走り 10 が表示される
-
transform
は 成功時と失敗時の処理をそれぞれ指定できる -
filter
は条件に合致しないと失敗扱い。 -
andThen
は指定した処理の結果を使わず、元の結果をそのまま次に送る (tap 的なもの)
複数の Future の扱いはまだちょっと整理できていないです。
scala - Wait for several Futures - Stack Overflow
Scala - Futureをマスターする -コンパニオンオブジェクト操作- - Qiita
-
sequence
: Seq[Future[T]] -> Future[Seq[T]] -
traverse
: Seq[T] -> Seq[Future[T]]
Scala の Promise
-
p.future
で Future を生成できる -
p.success
で Future を成功させられる、つまり onSuccessが呼ばれる -
p.failure
で Future を生成できる、つまり onFailure が呼ばれる - success か failure どちらか一回だけしか呼べない。2回呼ぶと呼んだ側で Exception が発生する
Promise の利用例
import scala.concurrent.{Await, Future, Promise}
import scala.concurrent.ExecutionContext.Implicits.global
def heavyFuture = {
val p = Promise[Int]
Future {
Thread.sleep(1000)
val result = 10
p.success(result)
}
p.future
}
val f = heavyFuture
f onSuccess { case x => println(x) }
まとめ:言語ごとの違いについてわかってきたこと
チェーンする対象の名前
今までなんとなく コールバックの入れ子を使わないで済むようにチェーンの仕組みを提供する責任は Promise が持っているものだと勘違いしていましたが、処理系によって結構呼び名がずれていました。
非同期処理の実行開始のタイミング
実行開始のタイミングがよくわかっていなかったのは Java のイメージ(Future作ってからExecutorでたたく)に引きずられていたから、みたいです。むしろ Future 作ったらそこで動き始めるイメージの方が合っているようです。
表にしてみました
処理系 | 実行開始のタイミング | チェーンする対象の名前 |
---|---|---|
Java5 | Future に処理を定義し、ExecutorService.execute に Future を渡した時 | チェーンできない |
Java8 | ComplatableFuture に処理を渡した時 | CompletionStage |
ECMAScript6 | Promise を new したとき? | Promise |
Deferred | Future の then を呼び出した(定義した)時? | Promise |
scala | Future を生成した時 | Future |
Promise と Future の関係のイメージ
「Promise が 一回だけの成功・失敗を保障する」 という説明も納得はしたのですが、Future とのセットでのとらえ方としては Future と Promise と独立した別物があるというよりも、
- Promise は Future の性質のこと
- Future には単体の Future と『Promise付きの Future』とがある
- Promise付きの Future が「一回だけ成功か失敗かする」
- 処理をチェーンしていく対象は Future
という理解が自分にはスッキリ来ました。多分色々な説明もそういうことを言っているのだと思いますが腹に落ちてなかったです ^^;
Promise の作り方のイメージ
- 何らかのコンテナ的なものの中で Promise を作っておく。
- コンテナの中で Future を作って、中で重い処理を実行して先ほどの Promise を終了させる。
- 受け側は コンテナから Promise を取り出して後続処理をチェーンしていく
処理系 | コンテナ的なもの | 重い処理の渡し方 | チェーン対象の取り出し方 | チェーン方法 |
---|---|---|---|---|
ECMAScript6 | Promise | p = new Promise(func(res,rej) { ...; res(r) }) |
p | p.then |
Deferred | $.Deferred | d = $.Deferred(func(success,err) { ...; success(r) }) |
p = d.promise |
p.then |
scala の Promise | Futureを作るための自作関数 | val p = Promise; Future { ...; p.success(r) } |
f = p.future |
f.map |
宿題
- Java8 の Promise の有無についてまだちゃんと調べていないことに気付きました。
- Play2Java や Rxも調べてみようと思いつつ放置