2013年5月8日水曜日

Future を使いこなす(3)

このエントリーをはてなブックマークに追加
目次
  • Future を使いこなす(1)
    • Future とは何か(おさらい)
    • 基本の Future の書き方
    • エラー発生時の処理を記述する
  • Future を使いこなす(2)
    • Future を続けて処理する
    • 連結時のエラー処理を実装する
    • リストの要素を Future で順に処理する
    • 複数の Future をまとめて待つ
  • Future を使いこなす(3)
    • Future を返す関数を実装する
    • Future をテストする

Future を返す関数を実装する

非同期処理の関数を実装するときには Future のインスタンスを返すようにします。 関数の中で別の非同期処理関数を呼び出して適切な Future を返すということが多いと思いますが、場合によっては自分で非同期処理を書いて Future を返すこともあるでしょう。

自分で Future を生成するときには Completer を使います。
import ('dart:async');

Future<String> asyncTask() {
  var c = new Completer<String>();

  // ここで非同期処理を実行する。

  // Completer#future を返す。
  return c.future;
}

void main() {
  asyncTask()
    .then(print)
    .catchError((e) => print('ERROR: $e'));
}
非同期処理が完了したことを通知するためには Completer#complete を使います。 同じように、エラーが発生したことを通知するためには Completer#completeError を使います。

では試しに、Timer を使って非同期処理を作ってみます。
import ('dart:async');

Future<String> asyncTask() {
  var c = new Completer<String>();

  // 非同期処理
  const ONE_SEC = const Duration(seconds: 1);
  new Timer(ONE_SEC, () {
    c.complete('Pass');
    // エラーの時は
    // c.completeError('Fail');
  });

  // Completer#future を返す。
  return c.future;
}

void main() {
  asyncTask()
    .then(print)
    .catchError((e) => print('ERROR: $e'));
}
実行すると 1 秒後に then で登録した関数が呼びだされ、結果が表示されます。

Future をテストする

自動テストを実行するには unittest ライブラリを使います。 pubspec.yaml に unittest を追加してください。 Dart Editor ならば GUI で設定できます。 テキストエディターで変更する場合は、次のように dependencies に unittest を追加します。
dependencies:
  unittest: any
できたら pub install を実行します(Dart Editor の場合は自動で実行されます)。

まず同期処理の場合のテストコードです。
import 'dart:async';
import 'package:unittest/unittest.dart';

String syncTask() => 'Pass';

void main() {
  test('Test a function', () {
    expect(syncTask(), equals('Pass'));
  });
}
これに対して非同期処理のテストコードは次のようになります。
import 'dart:async';
import 'package:unittest/unittest.dart';

Future<String> asyncTask() {
  var c = new Completer<String>();

  const ONE_SEC = const Duration(seconds: 1);
  new Timer(ONE_SEC, () => c.complete('Pass'));

  return c.future;
}

void main() {
  // テスト
  test('Test a future', () {
    asyncTask().then(
      expectAsync1(
        (e) => expect(e, equals('Pass'))));
  });
}
Future#then や Future#catchError などに expectAsync1(1 は引数の個数)で取得した関数を渡してテストします。 同期処理のテストと若干が違いますが、おおよそ似たような形で書くことができます。

ただし expectAsync1 は今後の言語仕様の変更にともなって呼び出し方が変更される可能性があります。 あまり気にすることはないと思いますが、覚えておくと良いかもしれません。

0 件のコメント:

コメントを投稿