2013年5月8日水曜日

Future を使いこなす(2)

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

Future を続けて処理する

ある Future の結果を元に、別の Future を使った処理を呼び出したいことがあります。その時は次のように書くことができます。
import ('dart:async');

Future<String> asyncTask1() { /* ... */ }
Future<String> asyncTask2(String s) { /* ... */ }

main() {
  Future<String> f1 = asyncTask1();
  Future<String> f2 = f1.then((s) {
    return asyncTask2(s);
  });
  f2.then((s) => print(s));
}
Future#then に指定したコールバック関数が Future のインスタンスを返す場合(つまりこのコードの return asyncTask2(s) のようになっている場合)、元々の Future(f1)と連結された新しい Future が then の戻り値(f2)となります。 したがってこのように書くと、asyncTask1 で生成された Future の処理が完了したあとに asyncTask2 で生成された Future が続けて処理されるようになります。

このコードは、より簡潔に次のように書くこともできます。
import ('dart:async');

Future<String> asyncTask1() { /* ... */ }
Future<String> asyncTask2(String s) { /* ... */ }

main() {
  asyncTask1()
    .then((s) => asyncTask2(s))
    .then((s) => print(s));

  // あるいは

  asyncTask1()
    .then(asyncTask2)
    .then(print);
}

連結時のエラー処理を実装する

Future を連結させた場合、どの Future で発生したエラーも最後の Future で受け取ることができます。 つまり次のように書けるということです。
import ('dart:async');

Future<String> asyncTask1() {
  // ここでエラーが発生しても...
}
Future<String> asyncTask2(String s) {
  // ここでエラーが発生しても...
}

main() {
  asyncTask1()
    .then(asyncTask2)
    .catchError((e) { /* ここでエラーが処理できる。 */ })
    .whenComplete(() { /* ここは最後に実行される。 */ });
}
見た目は少し異なりますが、try-catch と同じように扱えると考えれば迷わないでしょう。

リストの要素を Future で順に処理する

一つずつ Future を連結していくこともできますが、List のような Iterable な構造に入っているデータを順に Future で処理させるときには Future#forEach が使えます。
import ('dart:async');

Future asyncTask(String s) { /* ... */ }

main() {
  List<String> strs = ['a', 'b', 'c'];

  // strs の要素が一つずつ渡されて処理される。
  Future f = Future.forEach(strs, asyncTask);

  // 最後の要素が処理されると呼び出される。
  f.then((_) => print('Completed.'));
}
Future を then でつなげていった時と同じように、'a' の処理が終わったら 'b'、'b' の処理が終わったら 'c'、すべて終わったら最後の then という順番で処理されます。

途中の Future で値が返ってきてもそれらは捨てられてしまいますので、結果を受け取りたい場合には呼び出し元と呼び出し先で共通に参照できるオブジェクトを用意して、そこに結果を格納していくなどの方法を取る必要があります。

複数の Future をまとめて待つ

今度は Future をつなげて順に処理させるのではなく、複数の非同期で処理されている Future の完了をまとめて待つ方法です。 このような場合には Future#wait が使えます。
import ('dart:async');

Future asyncTask(String s) { /* ... */ }

main() {
  // List に複数の Future を入れておく。
  List<Future> fs = [
    asyncTask('a'),
    asyncTask('b'),
    asyncTask('c')
  ];

  // まとめて待つ。
  Future f = Future.wait(fs);
  f.then((_) => print('Completed.'));
}
List に入っている3つの Future はそれぞれ非同期に実行され、すべての Future が完了した時点で最後の Future の then に登録した関数が呼ばれます。 それぞれの結果はやはり捨てられますので、結果は別のオブジェクトに保存するなどしてください。

Future を使いこなす(3) へ続く

0 件のコメント:

コメントを投稿