はじめに
今まで何となくで使ってきていたのでしっかり確認しようと思い、以下を読んで個人的にまとめていきます。
この記事を通して分かること
- いつどのようにasync/awaitを使うか
- 実行順序にどのような影響があるか
非同期コードが重要な理由
非同期処理をすると、他の処理の終了を待っている間に処理を完了出来ます。
- 同期処理
- 完了するまで他の処理をブロックする
- 同期関数は、同期処理のみ実行
- 非同期処理
- 開始したら、それが完了する前に他の処理を実行できる
- 非同期関数は少なくとも1つの非同期処理を実行し、同期処理も実行可能
非同期処理を使う一般的なもの
- ネットからデータを取得する
- データベースに書き込む
- ファイルからデータを読み込む
Dartでは非同期処理を行うときにFutureクラスとasync/awaitを使用します。
futureとは?
- futureはFutureクラスのインスタンスで2つの状態を持つ
- 未完了(Uncompleted)
- 非同期処理を呼び出したとき、未完了のfutureを返す
- このfutureは非同期処理の完了を待つか、エラーを出す
- 完了(Completed)
- 非同期処理が成功すれば、futureは値(もしくはエラー)を持って完了する
- 未完了(Uncompleted)
-
Future<T>
のインスタンスはT型の値を作る- 例えば
Future<String>
であればString型の値を作る - もし値を持たないのであれば、
Future<void>
を使う
- 例えば
async/awaitとは?
非同期処理に使います。
基本的なガイドライン
- 非同期関数を定義するためには、関数本体の前にasyncを追加する
- awaitはasync関数の中でだけ使えるキーワード
実行の流れ
async関数は初めのawaitキーワードにたどり着くまでに、その間にある同期処理を実行します。つまり、await前にある全ての同期コードは即座に実行されます。
awaitの処理が終わるまで、それ以降のコードは実行されません。
同期処理と非同期処理
実際にコードを使って同期処理と非同期処理を確かめていきます。
同期処理
以下のコードは間違っています。後ほどasync/awaitで直しますが、どこが問題でしょうか?
String createOrderMessage() {
var order = fetchUserOrder();
return 'Your order is: $order';
}
Future<String> fetchUserOrder() =>
Future.delayed(
const Duration(seconds: 2),
() => 'Large Latte',
);
void main() {
print(createOrderMessage());
}
Your order is: Instance of '_Future<String>'
本当はYour order is: Large Latteになって欲しかったのですが、createOrderMessage()がfetchUserOrder()の終了を待たずに出力してしまっています。
非同期処理
上記のコードをasync/awaitを使えば適切に処理できます。
// Future<String>に変更!
Future<String> createOrderMessage() async { // async追加!
var order = await fetchUserOrder(); // await追加!
return 'Your order is: $order';
}
Future<String> fetchUserOrder() =>
Future.delayed(
const Duration(seconds: 2),
() => 'Large Latte',
);
Future<void> main() async { // async追加!
print('Fetching user order...');
print(await createOrderMessage()); // await追加!
}
Fetching user order...
Your order is: Large Latte
createOrderMessage()はfetchUserOrder()が終了するまで次の処理に進んでいないため、上手く処理されています。
その他の例
実行順序に関する例をもう一つ載せます。それぞれどのように出力されるか考えてみましょう。
コード
Future<void> printOrderMessage() async {
print('Awaiting user order...');
var order = await fetchUserOrder();
print('Your order is: $order');
}
Future<String> fetchUserOrder() {
return Future.delayed(const Duration(seconds: 4), () => 'Large Latte');
}
Future<void> main() async {
countSeconds(4);
await printOrderMessage();
}
void countSeconds(int s) {
for (var i = 1; i <= s; i++) {
Future.delayed(Duration(seconds: i), () => print(i));
}
}
Future<void> printOrderMessage() async {
var order = await fetchUserOrder(); // 順序を入れ替えた
print('Awaiting user order...'); // 順序を入れ替えた
print('Your order is: $order');
}
Future<String> fetchUserOrder() {
return Future.delayed(const Duration(seconds: 4), () => 'Large Latte');
}
Future<void> main() async {
countSeconds(4);
await printOrderMessage();
}
void countSeconds(int s) {
for (var i = 1; i <= s; i++) {
Future.delayed(Duration(seconds: i), () => print(i));
}
}
出力結果
Awaiting user order...
1
2
3
4
Your order is: Large Latte
1
2
3
4
Awaiting user order...
Your order is: Large Latte
おわりに
ざっくりとしたイメージは「awaitの部分で一旦止めてその処理が終わるまで次には進まない」感じですかね。今回の内容やコードは以下のものを使用させていただきました。
ここまで読んでいただきありがとうございました。何かありましたらコメントをお願いします。