dart:async ライブラリ

dart:async はDartにおける非同期プログラミングの中核機能を提供します:Future、Stream

  1. Future / Stream を利用する際、手動で dart:async をインポートする必要が必ずしもありません。dart:coreからエクスポートされています;
  2. StreamControllerStreamSubscriptionParallelWaitError などの型を使用する場合は明示的にインポートが必要です:
import 'dart:async';Code language: JavaScript (javascript)

二つの中核概念:

  • Future1回限りの非同期実行結果を表します(将来のいずれかのタイミングで値またはエラーが一度だけ出力される)
  • Stream連続した一連の非同期データを表します(複数の値が次々生成され、イベントストリームに類似)

Future

1. async / await

推奨される記法で、.then()によるチェーン呼び出しより可読性に優れます

  • async を付与した関数はFutureを返却します
  • await はFutureの完了を待機し、結果が返るまで現在のコード実行を一時停止;
  • try-catch で非同期例外を捕捉します。
// async関数はFutureを返す
Future<String> findEntryPoint() async => "main.dart";
Future<int> runExecutable(String entry, List args) async => 0;
Future<void> flushThenExit(int code) async {}

// await記法(推奨)
Future<void> runUsingAsyncAwait() async {
  try {
    var entryPoint = await findEntryPoint();
    var exitCode = await runExecutable(entryPoint, []);
    await flushThenExit(exitCode);
  } catch (e) {
    // 例外を一括捕捉
    print("エラー:$e");
  }
}Code language: JavaScript (javascript)

2. .then() / .catchError() 従来のチェーン記法

  • .then():Futureが正常完了した後にコールバックを実行。新しいFutureを返すため連鎖呼び出しが可能
  • .catchError():例外を捕捉;thenの戻り値に連鎖させる必要があります。そうしないとthen内部のエラーを捕捉できません
void runUsingFuture() {
  findEntryPoint()
      .then((entryPoint) {
        return runExecutable(entryPoint, []);
      })
      .then(flushThenExit)
      .catchError((e) {
        print("例外:$e");
      });
}Code language: JavaScript (javascript)

3. 複数の並列Futureを待機

① Future.wait()

すべてのタスクの完了を待ちます;いずれか一つのFutureがエラーを送出すると全体が即座に失敗します

Future<void> task1() async {}
Future<void> task2() async {}
Future<void> task3() async {}

void testWait() async {
  await Future.wait([
    task1(),
    task2(),
    task3(),
  ]);
  print("すべてのタスクが完了しました");
}Code language: JavaScript (javascript)
② List.wait / Record.wait(新機能、並行エラーを詳細に制御)

ParallelWaitError が送出され、成功したタスク・失敗したタスクを個別に判別可能。タスク同士が相互に中断されません。

Future<int> delete() async => 1;
Future<String> copy() async => "ok";
Future<bool> errorTask() async {
  throw Exception("タスク失敗");
}

void main() async {
  try {
    // リスト形式
    var results = await [delete(), copy(), errorTask()].wait;
  } on ParallelWaitError<List<Object?>, List<AsyncError?>> catch (e) {
    print(e.values);   // 成功時は値、失敗時はnull
    print(e.errors);   // 成功時はnull、失敗時はエラー情報を格納
  }

  // Recordタプル形式:型の異なるFutureに対応
  try {
    final (resInt, resStr, resBool) = await (delete(), copy(), errorTask()).wait;
  } on ParallelWaitError<(int?, String?, bool?), (AsyncError?, AsyncError?, AsyncError?)> catch(e) {

  }
}Code language: JavaScript (javascript)

Stream

Stream は連続した一連の非同期データを表します(ファイル1行ごとの読み込み、ボタンクリックイベント、ネットワークデータストリームなど)。

二つの利用方法

1:await for 非同期forループ(簡潔、同期的な走査に近い)

有限のデータストリームに適します;無限に継続するイベントストリーム(画面クリックイベントなど)には不向き。後続コードがブロックされます。

import 'dart:io';

Future<void> readFileByAwaitFor() async {
  var file = File("config.txt");
  Stream<List<int>> input = file.openRead();
  var lines = input
      .transform(utf8.decoder)
      .transform(const LineSplitter());

  try {
    await for (final line in lines) {
      print("1行読み込み:$line");
    }
    print("ファイル読み込み完了");
  } catch (e) {
    print("読み込みエラー $e");
  }
}Code language: PHP (php)
2:.listen() 購読監視(無限イベントストリーム向け)
inputStream.listen(
  (data) {
    // データ受信時のコールバック
  },
  onError: (err) {
    // エラー発生時
  },
  onDone: () {
    // ストリームが正常に終了
  },
);Code language: JavaScript (javascript)

listen は StreamSubscription を返し、.cancel() を呼び出すことで購読を解除可能です。

よく使われるStream操作メソッド

  • フィルタリング:where()
  • 要素取得:take()takeWhile()
  • 要素スキップ:skip()skipWhile()
  • 単一要素取得:firstlastsinglefirstWhere()

Streamデータ変換 transform()

トランスフォーマーを介しストリームのデータ型を変更(典型例:バイナリストリームを文字列へ変換)

var lines = inputStream
    .transform(utf8.decoder)       // List<int> → String
    .transform(const LineSplitter()); // String → 1行ごとの文字列Code language: JavaScript (javascript)

listen() と await for 選択指針

  1. ファイル読み込み、有限シーケンスデータ → await for
  2. UIイベント、継続配信メッセージなど無限ストリーム.listen()
  3. await for は開始後、ストリーム終了まで待機し続けます。無限イベントストリームでは処理が停止します

dart:async ライブラリ

Previous:

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です