Contents
Stream
Dart APIsのStreamオブジェクトは、複数の非同期処理の結果を表現します。例えば、ボタンのクリックのようなHTMLイベントはstreamを使って届けられます。ファイルもstreamとして読み込むことができます。
Using an asynchronous for loop
Stream APIの代わりに非同期なforループ(await for)を使うことができます。
sample1-1ではStreamクラスのlisten()メソッドでファイルのリストをリッスン(subscribe(購読))しています。listenメソッドの引数に、それぞれのファイルをサーチする関数リテラルを渡しています。
sample1-1の関数について考えましょう。
sample1-1
void main(List<String> arguments) { // ... FileSystemEntity.isDirectory(searchPath).then((isDir) { if (isDir) { final startingDir = Directory(searchPath); startingDir .list( recursive: argResults[recursive], followLinks: argResults[followLinks]) .listen((entity) { if (entity is File) { searchFile(entity, searchTerms); } }); } else { searchFile(File(searchPath), searchTerms); } }); }
isDirectoryメソッドがFuture<bool>型を返す。
–>
startingDir.list()がStream<
–>
startingDir.list().listen()
–>それぞれのイベントから受け取るファイル・ディレクトリなど(entity)を調べて、File型ならsearchFile()関数に渡す。
sample1-1と同じ挙動を示すコードをawaitを使って書きます。非同期なforループ(await for)を使うと、同期的なコードのように記述することができます。(sample1-2)
sample1-2
Future main(List<String> arguments) async { // ... if (await FileSystemEntity.isDirectory(searchPath)) { final startingDir = Directory(searchPath); await for (var entity in startingDir.list( recursive: argResults[recursive], followLinks: argResults[followLinks])) { if (entity is File) { searchFile(entity, searchTerms); } } } else { searchFile(File(searchPath), searchTerms); } }
awaitに関しての情報とDart言語の関連する機能については、asynchronous programming codelab.もご覧ください。
Listening for stream data
(streamの結果の)値が届いた時にそれらを取得するためには、
–> await forを使う。
–> listen()メソッドを使ってstreamを購読(subscribe)する。
の二つの方法があります。
sample2-1
// Find a button by ID and add an event handler. querySelector('#submitInfo').onClick.listen((e) { // When the button is clicked, it runs this code. submitData(); });
sample2-1では”submitInfo”ボタンのonClickプロパティがStreamインスタンスです。
もし一つのイベント(の結果)のみ必要なら、first,last,singleなどのプロパティを参照し、結果を取得することができます。結果を処理する前にテストしたい場合、firstWhere(),lastWhere(),singleWhere()などのメソッドを使うことができます。
streamの全イベントの一部分が必要な場合、skip(),skipWhile(),take(),takeWhile(),where()などのメソッドが使えます。
Transforming stream data
しばしば、streamの結果の値を使用する前に、結果の値のフォーマットを変更する必要があるシーンがあります。そのようなケースではtransform()メソッドを使用してデータの型が違うstreamを生成することができます。
var lines = inputStream .transform(utf8.decoder) .transform(LineSplitter());
上記のサンプルでは二つのtransformerを使っています。一番目はutf8.decorderで、Stream<int>→Stream<String>に変換します。二番目はLineSplitter()で、Stream<String>の各イベントの値を
工事中🏗
Handling errors and completion
エラーや完了を処理するコードの記述方法は、await forを使っているか、StreamAPIを使っているかによって異なってきます。
await forを使っている場合、try-catch文を使ってエラーハンドリングをします。streamが閉じた後に実行されるコードは、await forの後ろに来ます。
sample3-1
Future readFileAwaitFor() async { var config = File('config.txt'); Stream<List<int>> inputStream = config.openRead(); var lines = inputStream .transform(utf8.decoder) .transform(LineSplitter()); try { await for (var line in lines) { print('Got ${line.length} characters from stream'); } print('file is now closed'); } catch (e) { print(e); } }
sample3-1で、変数linesにセットされるのは、config.txtの一行ずつの文字列が、各イベントの結果として返されるstream。
Stream APIを使う場合、listenメソッドのonError引数にリスナー(コールバック関数)を設定します。streamが終了した時に実行されるコードはonDone引数にリスナー(コールバック関数)として設定します(sample3-2)。
sample3-2
var config = File('config.txt'); Stream<List<int>> inputStream = config.openRead(); inputStream .transform(utf8.decoder) .transform(LineSplitter()) .listen((String line) { print('Got ${line.length} characters from stream'); }, onDone: () { print('file is now closed'); }, onError: (e) { print(e); });
参考