ネットワーク通信、データベース読み書き、ファイルの入出力など、完了までに時間のかかる処理をUIスレッドで同期的に実行すると、アプリ画面がフリーズしてしまいます。
Dartはシングルスレッドのイベントループモデルを採用しており、処理をブロックすることなく非同期にタスクを実行するための仕組みとして Future と async / await を提供しています。
この章では、Dartのイベントループの概念から、非同期関数の書き方、エラーハンドリングまでを学びます。
Dartコードは単一のスレッド(シングルスレッド)上で動作します。処理が重い計算でスレッドを長時間占有すると、描画やタップ入力がブロックされてしまいます。
Dartはこの問題を イベントループ(Event Loop) によって解決しています。
+----------------------------+
| 現在実行中のコード |
+-------------+--------------+
| (完了)
v
+---------------------------+---------------------------+
| イベントループ (Event Loop) |
| |
| 1. マイクロタスクキュー (Microtask Queue) - 最優先 |
| [ Microtask 1 ] -> [ Microtask 2 ] |
| |
| 2. イベントキュー (Event Queue) - 通常の非同期タスク |
| [ I/O完了 ] -> [ タイマー発火 ] -> [ タップイベント ] |
+-------------------------------------------------------+
非同期処理(Future)をスケジューリングすると、現在のコードの実行が終わった後、イベントループが順番にそれを取り出して実行します。
Future の仕組みFuture<T> は、「将来のある時点で値 T またはエラーを返す非同期処理の結果」を表すオブジェクトです(JavaScriptの Promise や Rustの Future に相当します)。
then() による購読Futureには以下の3つの状態があります。
Future<String> fetchUserGreeting() {
return Future.delayed(
const Duration(milliseconds: 100),
() => 'こんにちは、ユーザーさん!',
);
}
void main() {
print('1. 処理開始');
fetchUserGreeting().then((greeting) {
print('3. データ受信: $greeting');
}).catchError((error) {
print('エラー: $error');
});
print('2. メイン関数の同期処理完了');
}dart run future_basics.dart1. 処理開始 2. メイン関数の同期処理完了 3. データ受信: こんにちは、ユーザーさん!
async / await による可読性の高い非同期コードasync と await キーワードを使うことで、非同期コードを同期コードと同じような直線的で読みやすいフローで記述できます。
async を付けると、その関数は自動的に Future を返す非同期関数になります。async 関数内でのみ、Future の完了を待機する await 式が使えます。Future<int> fetchUserId() async {
await Future.delayed(const Duration(milliseconds: 50));
return 42;
}
Future<String> fetchUserName(int id) async {
await Future.delayed(const Duration(milliseconds: 50));
return 'Alice (ID: $id)';
}
Future<void> displayUser() async {
print('ユーザー情報を取得中...');
final id = await fetchUserId();
final name = await fetchUserName(id);
print('取得完了: $name');
}
void main() async {
await displayUser();
}dart run async_await_demo.dartユーザー情報を取得中... 取得完了: Alice (ID: 42)
Future.wait による複数の非同期処理の並行実行互いに依存しない複数の非同期処理を同時に実行したい場合は、Future.wait を使います。
Future<String> fetchPostTitle() async => 'Dart 3の紹介';
Future<int> fetchLikeCount() async => 128;
void main() async {
// 並行して実行し、両方の完了を待つ
final results = await Future.wait([
fetchPostTitle(),
fetchLikeCount(),
]);
print('タイトル: ${results[0]}');
print('いいね数: ${results[1]}');
}dart run future_wait.dartタイトル: Dart 3の紹介 いいね数: 128
try-catch-finally)async / await を使った非同期処理では、同期コードと全く同じ try-catch-finally 構文で例外を捕捉できます。
Future<String> loadDataFromServer({required bool shouldFail}) async {
await Future.delayed(const Duration(milliseconds: 50));
if (shouldFail) {
throw Exception('ネットワーク接続が切断されました');
}
return '正常データレスポンス';
}
Future<void> handleRequest(bool shouldFail) async {
try {
print('リクエスト送信...');
final data = await loadDataFromServer(shouldFail: shouldFail);
print('成功: $data');
} catch (e) {
print('例外をキャッチ: $e');
} finally {
print('リソースクリーンアップ完了\n');
}
}
void main() async {
await handleRequest(false);
await handleRequest(true);
}dart run async_try_catch.dartリクエスト送信... 成功: 正常データレスポンス リソースクリーンアップ完了 リクエスト送信... 例外をキャッチ: Exception: ネットワーク接続が切断されました リソースクリーンアップ完了
この章では、Dartにおける非同期処理の基礎について学びました。
Future<T>: 将来確定する単一の非同期処理結果を表すオブジェクト。async / await: 直感的な構文で非同期コードを同期コードのように直線的に記述できる。Future.wait: 複数の非同期処理を並行して実行し、すべての完了を待機する。try-catch-finally 構文で同期処理と同様に例外をハンドリングできる。次の 第10章 では、単一の値ではなく「時間の経過とともに連続して発生するイベントのストリーム」を扱う Stream について学びます。
サーバーからユーザーデータを非同期に取得する関数 fetchUser を作成してください。
Future<Map<String, dynamic>> fetchUser(int id) を定義する。Future.delayed(Duration(milliseconds: 100)) で遅延を発生させる。id <= 0 の場合は Exception('無効なユーザーIDです: $id') をスローする。{'id': id, 'name': 'User_$id', 'points': 1500} を返す。main() で正常系(id: 1)と異常系(id: -1)の呼び出しを try-catch で処理し、結果を出力する。// ここに関数を定義してください
void main() async {
// ここで動作確認を行ってください
}dart run practice9_1.dart商品の価格情報と在庫情報を並行してフェッチし、合算結果を出力するプログラムを作成してください。
Future<int> fetchPrice(String productId): 50ミリ秒後に価格 3500 を返す。Future<int> fetchStock(String productId): 50ミリ秒後に在庫数 12 を返す。main() で Future.wait を使って2つの処理を同時に実行し、結果をレコードまたはリストとして受け取る。'商品ID: prod_abc, 価格: ¥3500, 在庫数: 12個' と出力する。// ここに関数を定義してください
void main() async {
// ここで動作確認を行ってください
}dart run practice9_2.dart