【Flutter/Dart】Futureで非同期処理の実装方法!await/asyncの使い方

【Flutter/Dart】Futureで非同期処理の実装方法!await/asyncの使い方

この記事からわかること

  • Flutter/DartFuture非同期処理実装する方法
  • await/async使い方
  • thenwaitメソッドなどの使い方

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

公式リファレンス:Asynchronous programming: Streams

Dartでの非同期処理

アプリ開発をしていく中で非同期処理の実装はマストになってきます。「非同期処理」とは処理の結果を待たずに並行して別の処理を進めるような処理のことです。例えばネットワーク通信やデータベースのCRUD操作、ファイルの読み書きなど処理に時間のかかる操作はアプリが一時的に停止してしまう原因になります。これらの処理を非同期で実行できるようにすることで、ユーザーはアプリをスムーズに操作し続けることができます。

反対の言葉で「同期処理」があります。これは処理が完了するまで次の処理には進まず、すべてを順番通りに実行する処理方法です。時間のかかる処理を同期的に行うと、アプリの応答が止まってしまいUXが低下してしまいます。

Dartにおける非同期プログラミングはFutureクラスとStreamクラス、async/awaitキーワードなどによって制御することが可能になっています。

Futureクラス

Future未来(future)に取得できる非同期処理の結果を管理するためのオブジェクトです。使い方は関数の返り値をFuture<任意のデータ型>で定義します。例えば以下は非同期にしたい処理(時間のかかる処理)ではなく同期的に実行しても問題ない処理を敢えてFutureにしてみた場合でこの場合はFuture.value()メソッドでレスポンスを行います。

/// 時間のかからない処理
Future<String> fetchDataNotAsync() {
  return Future.value('データ取得完了');
}

時間のかかる処理の場合はasync/awaitキーワードを使用します。例えば擬似的に再現するためにFuture.delayedを使用し遅延させてみます。asyncはメソッド自体にawaitは時間のかかる処理に記述します。どれでもつけれるわけではなく戻り値がFutureで定義されているメソッドで使用します。この場合はFuture<任意のデータ型>で定義しているデータ型をそのまま返却します。

// 時間のかかる処理
Future<String> fetchData() async {
  // 疑似的に2秒待機
  await Future.delayed(Duration(seconds: 2));
  return 'データ取得完了';
}

定義したfetchDataを呼び出すときも呼び出し側にasyncを付与し、awaitを使用して呼び出します。

void main() async {
  print('START');
  // 結果を取得できるまで待機
  String result = await fetchData();
  print(result);
  print('END');
}
// 出力
START
データ取得完了 // 2秒後
END

エラーハンドリング

Futureではエラーをスローすることができます。エラーをスローする方法はthrowで直接エラーを投げる方法とFuture.error()を使用する2パターンあります。

Future<String> fetchDataError() async {
  throw Exception('データ取得に失敗しました');
  // または以下
  // return Future.error('不明なエラー');
}

呼び出し側でエラーハンドリングを行うためにはtry ~ catch文を使用してエラーをキャッチします。

void main() async {
  print('START');
  try {
    String result = await fetchDataError();
    // エラーになると以下は呼ばれない
    print(result);
  } catch (e) {
    print('エラー発生: $e');
  }
  print('END');
}

thenメソッド

非同期処理をawaitを使わずに結果後の処理をコールバックで記述できるのがthenメソッドです。クロージャーから結果を取得でき、処理を記述することができ、呼び出し側でもasyncの付与が不要になります。

void main() {
  print('START');
  fetchData().then((value) {
    print('Success: $value');
  });
  print('END');
}

// 出力
START
END
Success: データ取得完了 // 2秒後

thenメソッドチェーンで処理を連結させることも可能です。

Future<int> calc() {
  return Future.delayed(Duration(seconds: 1), () => 10);
}

void main() {
  calc()
    .then((value) => value * 2)
    .then((result) => print('結果: $result')); // 結果: 20
}

catchErrorメソッド

thenで結果を取得する際のエラーハンドリングはcatchErrorメソッドで行います。

Future<String> fetchError() {
  return Future.error('エラー発生');
}

void main() {
  fetchError()
      .then((value) => print('成功: $value'))
      .catchError((e) => print('失敗: $e'));
}

waitメソッド

waitメソッドは複数の非同期処理(返り値がFuture)を並列で実行し、すべての結果を待機して取得することができます。処理は並列に実行されるため、最も遅い非同期処理が完了したタイミングで結果を取得することができます。

void main() async {
  final results = await Future.wait([
    // 遅延処理1
    Future.delayed(Duration(seconds: 1), () => 'A'),
    // 遅延処理2
    Future.delayed(Duration(seconds: 4), () => 'B'),
  ]);
  // 上記の場合4秒後に以下の結果を取得できる
  print(results); // ['A', 'B']
}

waitの引数にはIterable<Future>型を返す処理を指定することができるるのでListSetなどを使用することが可能です。

forEachメソッド

forEachメソッドは指定したコレクション要素の中身に対して非同期処理を繰り返し行うことができます。コレクションはIterable<T>型で指定することができ、クロージャーの引数で各要素を順番に参照することができます。

void main() async {
  await Future.forEach([1, 2, 3], (num) async {
    await Future.delayed(Duration(seconds: 1));
    print('処理: $num');
  });
}

delayedメソッド

delayed非同期処理内で遅延処理を行うメソッドです。引数にはDuration型で遅延させたい時間を指定します。2つ目の引数には指定時間後に実行させたい処理を指定することもできます。

// 1秒遅延
await Future.delayed(Duration(seconds: 1));
// 500ミリ秒遅延
await Future.delayed(Duration(milliseconds: 500));
// 1秒遅延後に任意の処理を実行
final result = await Future.delayed(
  Duration(seconds: 1),
  () => '完了データ',
);

timeoutメソッド

timeout指定時間を超えたらタイムアウトさせるメソッドです。引数にはタイムアウトを設ける時間をDuration型で指定します。タイムアウトするとTimeoutExceptionがスローされます。


Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 3));
  return 'データ取得完了';
}

void main() async {
  try {
    final result = await fetchData().timeout(Duration(seconds: 2));
    print(result);
  } catch (e) {
    // TimeoutException after 0:00:02.000000: Future not completed
    print('タイムアウト: $e');
  }
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article