LoginSignup
2
2

More than 1 year has passed since last update.

【Flutter】非同期プログラミング:futures, async, await

Posted at

はじめに

今まで何となくで使ってきていたのでしっかり確認しようと思い、以下を読んで個人的にまとめていきます。

この記事を通して分かること

  • いつどのようにasync/awaitを使うか
  • 実行順序にどのような影響があるか

非同期コードが重要な理由

非同期処理をすると、他の処理の終了を待っている間に処理を完了出来ます。

  • 同期処理
    • 完了するまで他の処理をブロックする
    • 同期関数は、同期処理のみ実行
  • 非同期処理
    • 開始したら、それが完了する前に他の処理を実行できる
    • 非同期関数は少なくとも1つの非同期処理を実行し、同期処理も実行可能

非同期処理を使う一般的なもの

  • ネットからデータを取得する
  • データベースに書き込む
  • ファイルからデータを読み込む

Dartでは非同期処理を行うときにFutureクラスとasync/awaitを使用します。

futureとは?

  • futureはFutureクラスのインスタンスで2つの状態を持つ
    • 未完了(Uncompleted)
      • 非同期処理を呼び出したとき、未完了のfutureを返す
      • このfutureは非同期処理の完了を待つか、エラーを出す
    • 完了(Completed)
      • 非同期処理が成功すれば、futureは値(もしくはエラー)を持って完了する
  • 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()が終了するまで次の処理に進んでいないため、上手く処理されています。

その他の例

実行順序に関する例をもう一つ載せます。それぞれどのように出力されるか考えてみましょう。

コード

パターン1
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));
  }
}
パターン2
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));
  }
}

出力結果

パターン1
Awaiting user order...
1
2
3
4
Your order is: Large Latte
パターン2
1
2
3
4
Awaiting user order...
Your order is: Large Latte

おわりに

ざっくりとしたイメージは「awaitの部分で一旦止めてその処理が終わるまで次には進まない」感じですかね。今回の内容やコードは以下のものを使用させていただきました。

ここまで読んでいただきありがとうございました。何かありましたらコメントをお願いします。

2
2
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
2
2