2024/11/16 Kotlinの非同期フロー
.mapや.filterなどの一般的なメソッドを適用できる
これらのメソッドもSequenceを返す
code:sequence.kt
fun values() = sequence {
yield(1);
println("1 yielded");
yield(2);
println("2 yielded"); // <-- 表示されない?
}
fun main() = runBlocking {
val iter = values().iterator();
for (i in 0..1) {
println(iter.next());
}
}
SequenceScope内でyieldを呼び出すと、blockの実行が一時中断し、制御がいったん離れる。
次にIterator.next()などで値が要求されたときに再開される。
そのため、「1」「1 yielded」「2」と表示される。
Kotlin Playgroundでは「2 yielded」が表示されなかったが、なぜだろうか。。
yield(2)での中断が再開されず、制御が戻っていないということ?
sequenceにとってsuspend関数の呼び出しは、自身が値を外部へ送出する唯一の手段である 言い換えると、suspendやresumeは、値の送出以外のタイミングで発生してはならない
実際、SequenceScopeのblock内では、使用できるsuspend関数の種類に制限がある。
つまり、値が要求されていないにもかかわらずblockの実行が再開してしまうことは、sequenceの仕組みに反することになる
したがって、Sequenceの呼び出し側が値を要求していないなら、たとえ途中でプログラムが終了しようとも、blockの実行は再開されるべきではない。
sequenceと異なり、値の処理に使用するや .map にはsuspend関数を渡すことができる。
code:flow.kt
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun values(): Flow<Int> = flow {
emit(1);
println("1 yielded");
emit(2);
println("2 yielded");
}
fun main() = runBlocking {
values().collect { println(it) }
}
出力は「1」「1 yielded」「2」「2 yielded」となる
emit(2)の後にあるprintln("2 yielded")も実行されている
今回は .collect が終端演算子
このような挙動が成立するのは、Flowのメソッドがsuspend関数だからであろう
この動作は、.bufferメソッドを使うことで変更することができる。
flow {}のブロックと、終端演算子のブロックが同時に実行される。
出力は「1 yielded」「2 yielded」「1」「2」となる
flow {}のブロックを非同期的に実行することで、終端演算子による値の消費を待つことなく値をメモリ上に保存している
SharedFlow
通常のFlowは、終端演算子を実行するたびに個別にFlowが実行される。
SharedFlowは、値がemitされるたびに、すべてのFlowCollectorに値が行き渡る
SharedFlowの仕組みを使うと任意のイベントを通知することができるが、StateFlowは状態の更新を通知することに特化している。
Collects values from this StateFlow and represents its latest value via State. The StateFlow.value is used as an initial value. Every time there would be new value posted into the StateFlow the returned State will be updated causing recomposition of every State.value usage.
元のStateFlowにもcollectメソッドはあるが、Android開発で使いやすいよう、現在の状態のみを取得するメソッド
説明のとおり、内部ではStateFlow.collectで値をcollectしているのだろう