このコードを拝見した限りでは、次のような順序(時系列)で実行されることがとなっていそうな気がします。
-
funcX
の、少なくともThreadLocal
でデータを詰め込むところまでが実行される -
funcX
の残りの処理とfuncY
が並列実行される- このとき
funcX
の残りの処理が終了することを待つ(同期する)必要はない
- このとき
-
funcY
が終ってからfuncZ
を実行する
したがって、このような同期タイミングを利用すれば副作用的なThreadLocal
を利用しなくてもよさそうに思いました。まず前提としてThreadLocal
に突っ込んでおきたい(= funcZ
であとから使いたい)型をA
として
def funcXPhase1: Future[A] // funcZで使う型`A`な値をつくるまで
def funcXPhase2: Future[?] // 型`A`な値を作ったあと
def funcX(): Future[?] = {
funcXPhase1 flatMap { _ =>
funcXPhase2
}
}
という となっているものとします。そうすれば
for {
a <- funcXPhase1
y <- (funcXPhase2 zip Future(funcY())) {
// 並列実行のためにzipするが`funcXPhase2`の返り値は捨てる
case (_, y) => y
} recover {
// ナイスキャッチ
case _ => funcZ(a, false)
}
z <- funcZ(a, true)
} yield z
というようにできるのではないでしょうか。この例だと正常系な場合はfuncXPhase2
が終わってからfuncZ
をはじめているので、元のコードとは多少意味が違っているので、そこをもっと再現する必要はあるかもしれませんが、そこまで複雑なコードにせずに状態を排除できているのでいったんこれでとしました。
Like!