0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Roadmap.shで学ぶFlutter - Advanced Dart - [Core Libraries②: async]

Posted at

導入

前回に続いて、roadmap.shのFlutterのAdvanced DartとしてCore Librariesの項目の2番目のasyncについてです。

FlutterはAPIサーバーとのやりとりも多いと考えられるので、通信処理においてasyncを利用することはしばしばあると思われます。
このasyncをマスターすることはモバイルアプリ開発において重要であることは間違いありません。では、内容に入っていきます。

dart:async

非同期プログラミングとDartのFutureおよびStream

Dartでは、非同期プログラミングによく使われるコールバック関数の代わりに、FutureStream オブジェクトを使用します。

Future は将来のある時点で提供される結果の約束のようなものであり、Stream はイベントなどの一連の値を取得する方法です。

これらのオブジェクトは dart:async ライブラリに含まれています。

Dart言語は、asyncawait といったキーワードを使用した非同期コーディングをサポートしています。FutureStream APIを直接使う必要は必ずしもありません。また、dart:core がこれらのクラスをエクスポートしているため、dart:async をインポートする必要はありません。

Future

Future オブジェクトは、非同期メソッドによって返されるオブジェクトとしてDartライブラリに広く存在します。Future が完了すると、その値が使用可能になります。

await の使用

Future API を直接使用する前に、await を使うことを検討してください。await 式を使用するコードは、Future API を使うコードよりも理解しやすいことがあります。

Future<void> runUsingAsyncAwait() async {
  // ...
  var entryPoint = await findEntryPoint();
  var exitCode = await runExecutable(entryPoint, args);
  await flushThenExit(exitCode);
}

async 関数は、Future からの例外をキャッチすることができます。

var entryPoint = await findEntryPoint();
try {
  var exitCode = await runExecutable(entryPoint, args);
  await flushThenExit(exitCode);
} catch (e) {
  // エラー処理...
}

基本的な使用法

then() メソッドを使用して、Future が完了したときに実行されるコードをスケジュールできます。

httpClient.read(url).then((String result) {
  print(result);
}).catchError((e) {
  // エラー処理...
});

複数の非同期メソッドのチェーン

then() メソッドは Future を返し、複数の非同期関数を特定の順序で実行するための便利な方法を提供します。

Future result = costlyQuery(url);
result
    .then((value) => expensiveWork(value))
    .then((_) => lengthyComputation())
    .then((_) => print('Done!'))
    .catchError((exception) {
  /* エラー処理... */
});

上記の例を await を使って書くと、以下のようになります。

try {
  final value = await costlyQuery(url);
  await expensiveWork(value);
  await lengthyComputation();
  print('Done!');
} catch (e) {
  /* エラー処理... */
}

複数の Future の待機

多くの非同期関数を呼び出してそれらがすべて完了するのを待つ必要がある場合は、Future.wait() メソッドを使用します。
この機能は便利そうです!

await Future.wait([
  deleteLotsOfFiles(),
  copyLotsOfFiles(),
  checksumLotsOfOtherFiles(),
]);
print('Done with all the long steps!');

複数の Future のエラーハンドリング

複数の非同期操作を並列に実行し、それぞれの結果を処理する場合には、wait メソッドを使用してエラーを処理することができます。

try {
  var results = await [delete(), copy(), errorResult()].wait;
  // 処理...
} on ParallelWaitError<List<bool?>, List<AsyncError?>> catch (e) {
  // エラーハンドリング...
}

個別の結果が必要な場合には、レコードを使用して wait メソッドを呼び出すことができます。

try {
  (int, String, bool) result = await (delete(), copy(), errorResult()).wait;
  // 処理...
} on ParallelWaitError<(int?, String?, bool?),
    (AsyncError?, AsyncError?, AsyncError?)> catch (e) {
  // エラーハンドリング...
}

Stream

Stream オブジェクトは、DartのAPI全体にわたって使用されており、データのシーケンスを表します。たとえば、ボタンのクリックなどのHTMLイベントはストリームを使用して配信されます。また、ファイルをストリームとして読み取ることも可能です。

非同期forループの使用

非同期forループ (await for) は、Stream API の代わりに使用できる場合があります。

void main(List<String> arguments) async {
  if (await FileSystemEntity.isDirectory(searchPath)) {
    final startingDir = Directory(searchPath);
    await for (final entity in startingDir.list()) {
      if (entity is File) {
        searchFile(entity, searchTerms);
      }
    }
  } else {
    searchFile(File(searchPath), searchTerms);
  }
}

ストリームデータのリスニング

await for を使用するか、listen() メソッドを使用してストリームにサブスクライブします。

submitButton.onClick.listen((e) {
  submitData();
});

ストリームデータの変換

transform() メソッドを使用して、ストリームのデータ形式を変換できます。

var lines = inputStream
        .transform(utf8.decoder)
        .transform(const LineSplitter());

エラーと完了の処理

await for を使用する場合、try-catch を使用してエラーを処理し、ストリームが閉じられた後に実行されるコードを記述します。

Future<void> readFileAwaitFor() async {
  var config = File('config.txt');
  Stream<List<int>> inputStream = config.openRead();

  var lines = inputStream
          .transform(utf8.decoder)
          .transform(const LineSplitter());
  try {
    await for (final line in lines) {
      print('Got ${line.length} characters from stream');
    }
    print('file is now closed');
  } catch (e) {
    print(e);
  }
}

Stream API を使用する場合、onError リスナーを登録してエラーを処理し、ストリームが閉じられた後のコードを onDone リスナーに登録します。

inputStream.transform(utf8.decoder).transform(const LineSplitter()).listen(
  (String line) {
    print('Got ${line.length} characters from stream');
  }, onDone: () {
    print('file is now closed');
  }, onError: (e) {
    print(e);
  });

終わりに

今回は、Dartのasyncに関する基本的な概念と使い方の紹介しました。非同期プログラミングは、モバイルアプリ開発において効率的かつスムーズなユーザーエクスペリエンスを提供するために重要な要素です。FutureStreamを活用して、安定したアプリケーションを構築していきたいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?