Dart Streamクラスの概要ついてメモ書き。公式はこちら。
Streamは非同期処理を扱うクラス。dart:asyncライブラリで提供している。dart:asyncライブラリはdart:coreライブラリを通して使用できる。
非同期を扱うクラスにFutureがある。FutureとStreamの違いとして、Futureは単発イベントで使用するのに対して、Streamは連続したイベントを扱う場合に使用する。
例として、非同期ジェネレータのasync*関数でStreamを返してみる。
通常関数は1つの戻り値を返すが、ジェネレータ関数は複数の戻り値を返せる。
async*関数は”return”ではなく”yield”を使って戻り値を返す。コーディングで確認。
void main() {
Stream<String> test() async* {
List<String> ts = ["a", "b", "c"];
for (var i in ts) {
// Streamオブジェクトとしてts配列の値を返す。
yield i;
}
}
// listen()でStreamSubscriptionオブジェクトを受け取る。
test().listen((event) {
print(event);
});
}
実行結果。
a
b
c
test()関数はStream<String>オブジェクトとして”a”, “b”, “c”を1文字づつ返している。受信側のlisten()にはイベントとしてStreamSubscriptionオブジェクトが引数として渡される。このオブジェクトを受け取ることでtest()関数の戻り値を使用できる。
Streamはデータを分割送信でき、かつlisten()で受け取ったデータはすぐに使用できる。動画のストリーミング再生、チャット、大容量データの分割送信等をイメージするとStreamの使用用途が理解できると思う。
話は逸れるが、以下のように別途定義したジェネレータ関数の値を戻り値を返す場合は、”yield*”を使用する。
void main() {
Stream<int> test() async* {
yield* testInt();
}
test().listen((event) {
print(event);
});
}
Stream<int> testInt() async* {
for (int i = 0; i < 3; i++) {
yield i;
}
}
実行結果。
0
1
2
await for文を使うこともできる。この場合はyieldを使用する。
void main() {
Stream<int> test() async* {
await for (int i in testInt()) {
yield i;
}
;
}
test().listen((event) {
print(event);
});
}
Stream<int> testInt() async* {
for (int i = 0; i < 3; i++) {
yield i;
}
}
話をStreamに戻す。
Streamは2種類ある。一つは”Single-subscription”で、もう一つは “broadcast” だ。
Single-subscriptionはリスナーと一対一の通信をする場合に使用する。リスナーが準備できていなければ送信できず、またリスナーがクローズするとそれ以上データを送信できない。Single-subscriptionはasync*関数によって生成され、関数を実行する度に新しいStreamが生成される。実行中にリスナーを新しく生成した場合は、最初に生成したリスナーはキャンセルされてしまう。ファイルI/Oの受信など、大きな連続データをストリーミングする時に使用する。
Single-subscriptionを複数のリスナーが同時に受信する場合はasBroadcastStreamを使用する。
broadcastは複数のリスナーを相手に通信ができ、リスナーがいなくてもStreamイベントを発行できる。ただし、Streamイベント発行中にリスナーが追加された場合、そのリスナーは発行中のイベントを受信できない。また、リスナーがイベントの受信をキャンセルすると、受信は即時停止する。
“done”イベントを発行すると、リスナーのサブスクリプションは解除される。
Streamは”pause”リクエストを優先する。pauseはバッファリングを伴う場合があるが、処理を一時停止できる。
broadcastかそうでないかはisBroadcastプロパティで判断する。isBroadcastがtrueの場合はbroadcastとして動作している。
コンストラクタ等の詳細については別途記載する。
コメント