🚀
【Dart】Futureクラスとasync/awaitの基本的な使い方
簡単なFutureクラスの利用方法と、非同期処理について実行結果とともに確認します。
【Dart】非同期処理の基礎
はじめに
-
【参考】公式サイト
-
実行環境
-
DartPadやAndroid Studio等で実行
- Dart SDK 2.10.4
-
DartPadやAndroid Studio等で実行
事前知識
- 時間が掛かる処理を行う場合に、次の処理(画面出力等)を行いつつ、
その処理を待つ必要がある=非同期処理 - 非同期処理方法は以下が存在
- Futureクラスの
then
関数を利用する方法 - asyncとawaitキーワードを利用する方法
- Futureクラスの
- 非同期処理方法は、「asyncとawait」が推奨されている
- ネストが少なく、通常の同期処理と同様に記載可能であり、可読性が高いため
Futureについて
- 通常の同期処理
- 時間が掛かる処理を行う場合に次の処理(画面出力等)が止まってしまう
- ※以下はpickAllDocumentsは時間が掛かる処理とする
- 「
print('Next...');
」は、前処理が完了した後に実行されるため、出力されるまでに時間が掛かる
- 時間が掛かる処理を行う場合に次の処理(画面出力等)が止まってしまう
String pickAllDocuments() {
String docs;
// 時間が掛かる処理と仮定
for (int i = 0; i < 100000; ++i) {
if (i == 99999) docs = 'Finish pickAllDocuments!';
}
return docs;
}
void main() {
final docs = pickAllDocuments();
print(docs);
print('Next...');
}
実行結果
Finish pickAllDocuments!
Next...
- 時間が掛かる関数の返り値の型をFutureとする
- 変更箇所:2行
-
String pickAllDocuments() {
→Future<String> pickAllDocuments() {
-
return docs;
→return Future<String>.value(docs);
-
- Futureのインスタンスが即座に変数に格納され、次の処理に進む
- 変更箇所:2行
Future<String> pickAllDocuments() {
String docs;
// 時間が掛かる処理と仮定
for (int i = 0; i < 100000; ++i) {
if (i == 99999) docs = 'Finish pickAllDocuments!';
}
return Future<String>.value(docs);
}
void main() {
final docs = pickAllDocuments();
print(docs);
print('Next...');
}
実行結果
Instance of '_Future<String>'
Next...
then
関数を利用
非同期処理:Futureクラスの- Futureクラスの
then
関数を利用し、時間が掛かる関数の値処理が終わる前に次の処理実施-
then
関数の引数として与えたコールバック関数は、時間が掛かる関数の値処理が終わった際に実行
-
Future<String> pickAllDocuments() {
String docs;
// 時間が掛かる処理と仮定
for (int i = 0; i < 100000; ++i) {
if (i == 99999) docs = 'Finish pickAllDocuments!';
}
return Future<String>.value(docs);
}
void main() {
final docs = pickAllDocuments();
docs.then((value) => print(value));
print('Next...');
}
実行結果
Next...
Finish pickAllDocuments!
- Futureクラスの
then
関数を利用し、時間が掛かる関数の値処理が終わった際に次の処理実施-
then
関数の中に複数行追加すると、上から順次処理
-
Future<String> pickAllDocuments() {
String docs;
// 時間が掛かる処理と仮定
for (int i = 0; i < 100000; ++i) {
if (i == 99999) docs = 'Finish pickAllDocuments!';
}
return Future<String>.value(docs);
}
void main() {
final docs = pickAllDocuments();
docs.then((value) {
print(value);
print('Next...');
});
}
実行結果
Finish pickAllDocuments!
Next...
非同期処理:asyncとawaitキーワードを利用
- asyncとawaitキーワードを利用し、時間が掛かる関数の値処理が終わった際に次の処理実施
- 方法
import 'dart:async';
を追加-
main
関数にasyncキーワードを追加 -
pickAllDocuments
関数にawaitキーワードを追加
- 注意
- awaitの後ろのコードは、Futureの処理(時間が掛かる関数の値処理)が完了後に実施
- 方法
Future<String> pickAllDocuments() {
String docs;
// 時間が掛かる処理と仮定
for (int i = 0; i < 100000; ++i) {
if (i == 99999) docs = 'Finish pickAllDocuments!';
}
return Future<String>.value(docs);
}
void main() async {
final docs = await pickAllDocuments();
print(docs);
print('Next...');
}
実行結果
Finish pickAllDocuments!
Next...
- asyncとawaitキーワードを利用し、時間が掛かる関数の値処理が終わる前に次の処理実施
- 処理の前後が関連するひとまとまりの関数にasyncを追加(例:以下test関数に分離)
Future<String> pickAllDocuments() {
String docs;
// 時間が掛かる処理と仮定
for (int i = 0; i < 100000; ++i) {
if (i == 99999) docs = 'Finish pickAllDocuments!';
}
return Future<String>.value(docs);
}
void test() async {
final docs = await pickAllDocuments();
print(docs);
}
void main() {
test();
print('Next...');
}
実行結果
Next...
Finish pickAllDocuments!
エラーハンドリング
- Futureクラスの
then
関数を利用の際- try-catchでエラーをキャッチ
Future<String> pickAllDocuments() {
String docs;
// 時間が掛かる処理と仮定
for (int i = 0; i < 100000; ++i) {
throw Exception();
if (i == 99999) docs = 'Finish pickAllDocuments!';
}
return Future<String>.value(docs);
}
void main() {
try {
final docs = pickAllDocuments();
docs.then((value) {
print(value);
print('Next...');
});
} on Exception catch (e) {
print('TEST:' + e.toString());
}
}
実行結果
TEST:Exception
- Futureクラスの
then
関数を利用の際- Futureクラスの
catch
関数を利用し、エラーをキャッチ
※then
関数の中でのエラーをキャッチ(pickAllDocuments
関数でのエラーはキャッチ不可)
- Futureクラスの
Future<String> pickAllDocuments() {
String docs;
// 時間が掛かる処理と仮定
for (int i = 0; i < 100000; ++i) {
if (i == 99999) docs = 'Finish pickAllDocuments!';
}
return Future<String>.value(docs);
}
void main() {
final docs = pickAllDocuments();
docs.then((value) {
throw Exception();
print(value);
print('Next...');
}).catchError((e) => print('TEST:' + e.toString()));
}
実行結果
TEST:Exception
- asyncとawaitキーワードを利用
- try-catchでエラーをキャッチ
Future<String> pickAllDocuments() {
String docs;
// 時間が掛かる処理と仮定
for (int i = 0; i < 100000; ++i) {
throw Exception();
if (i == 99999) docs = 'Finish pickAllDocuments!';
}
return Future<String>.value(docs);
}
void main() async {
try {
final docs = await pickAllDocuments();
print(docs);
} on Exception catch (e) {
print('TEST:' + e.toString());
}
print('Next...');
}
実行結果
TEST:Exception
Next...
【備考】Futureのコンストラクタ
Future.value
- Futureオブジェクトをすぐに返す
void main() {
final future = Future<String>.value('TEST');
future.then((value) => print(value));
print(future);
}
実行結果
Instance of '_Future<String>'
TEST
- 基本的には、時間が掛かる格納処理を行うような場合に利用
void main() {
String str;
// 時間が掛かる処理と仮定
for (int i = 0; i < 100000; ++i) {
if (i == 99999) str = 'TEST';
}
final future = Future<String>.value(str);
future.then((value) => print(value));
print(future);
}
実行結果
Instance of '_Future<String>'
TEST
- asyncを利用し、Futureの値をwrapする方法をどちらかというと推奨(文字量が少ないため)
void main() {
Future<int> test1() => Future<int>.value(1);
Future<int> test2() async => 10;
test1().then((value) => print(value));
test2().then((value) => print(value));
}
実行結果
1
10
Future.delayed
- 指定した遅延の後に処理を実施
- 3秒後に「TEST」と出力
void main() {
final future =
Future<String>.delayed(const Duration(seconds: 3), () => 'TEST');
future.then((value) => print(value));
print(future);
}
実行結果
Instance of '_Future<String>'
TEST
- 簡単に3秒待って、次の処理を実施
- 3秒後に「Next...」と出力
void main() async {
await Future.delayed(Duration(seconds: 3));
print('Next...');
}
実行結果
Next...
Future.error
- Futureオブジェクトをすぐに返すが、その後エラー終了
void main() {
final future = Future<String>.error('Test:ERROR');
future.then((value) => print(value));
print(future);
}
実行結果
Instance of 'Future<String>'
Unhandled Exception: Test:ERROR
- オプションでスタックトレース追加可能
void main() {
final future = Future<String>.error(
'Test:ERROR', StackTrace.fromString('Test:Stack Trace'));
future.then((value) => print(value));
print(future);
}
実行結果
Instance of 'Future<String>'
Unhandled Exception: Test:ERROR
Test:Stack Trace
Future.sync
- 一見、valueと変わらなく見える
void main() {
final future = Future<String>.sync(() => 'TEST');
future.then((value) => print(value));
print(future);
}
実行結果
Instance of '_Future<String>'
TEST
- 特徴としては、
sync
に与えたコールバック関数が即座に実施
void main() {
final future = Future<String>.sync(() {
print('TEST');
return 'TESTTEST';
});
future.then((value) => print(value));
print(future);
}
実行結果
TEST
Instance of '_Future<String>'
TESTTEST
Discussion