[Flutter]非同期処理で値を取得するまで待機する Completer テストコード付き

非同期を含めた処理を書いていると、非同期処理が完了してから、メインの処理を続けたいときがある。もしくは、非同期処理の結果を、メインの処理に表示したいときがある。そんなときに、Completerが役に立つ。

実際に使ったこと

私が実際にCompleterを使った一例は、以下の通りです。

  • 3つの音を連続して再生させるために、それぞれの音の完了時に、次の音の開始前にCompleterで待機した
  • Firestoreから取得したデータを画面に表示するため、データ取得まで画面描画を待機させ、取得後に表示項目に初期値を渡した

ソース

import 'dart:async';

import 'package:flutter_test/flutter_test.dart';

void main() async {
  test('completer', () async {
    Completer completer = Completer();//2

    List result = [];

     // 1
    Future.delayed(const Duration(seconds: 1)).then((value) {
      result.add('delayed'); 
      expect(completer.isCompleted, false);
      completer.complete('delay finished');//3
      expect(completer.isCompleted, true);
    });

    result.add('before future'); 
    result.add(await completer.future); //4
    result.add('after future'); 

    expect(result.length, 4);
    expect(result[0], 'before future');
    expect(result[1], 'delayed');
    expect(result[2], 'delay finished');
    expect(result[3], 'after future');
  });
}
  1. Future.delayed
  2. 実際には、メインの処理の前に実施したい同期処理を記載してください。

  3. Completer completer = Completer()
  4. Completerを定義します。ジェネリックで、結果に何の値を入れたいか決めてください。

  5. completer.complete
  6. 結果を引数にして、Completerの処理を完了させてください。

  7. await completer.future
  8. Completer.futureでFuture型を返します。Future型をそのまま戻して、もっと後の処理で使うこともできます。ここでは、await を使って非同期処理が完了するのを待ってから、メインの処理の続きを行うようにしてます。

  9. completer.isCompleted
  10. Completerの処理が完了しているか、まだ処理中かを取得することができます。

まとめ

そんなわけで、Completerの説明をしました。最初、音の連続再生時に使用しました。そのときは、音の再生が完了したらイベントが発生する、という処理内にCompleter.completeを入れなければなりませんでした。しかし、非同期処理やCompleterがよく分かっていなかったので、3つの音が同時に鳴らしたり、音のイベント周りもよく分からず、苦労しました。
この記事を読んだ方のお役に立てれば幸いです。役に立ってSNSにシェアしていただけると、非常に喜びます!