2013年8月28日水曜日

Dart Editor をプロキシ経由で更新する

このエントリーをはてなブックマークに追加
Dart Editor はメニューの Tools - Preferences あるいは Help - About Dart Editor で更新できますが、プロキシ経由でインターネットにアクセスする環境では、更新サーバーに接続できずエラーになる場合があります。この場合は次の方法を試してみてください。
  1. Dart Editor を終了する。
  2. Dart Editor の実行ファイルと同じ場所にある DartEditor.ini ファイルをテキストエディタで開く。
  3. 「-Dhttp.proxyHost=」「-Dhttp.proxyPort=」という行を追加し、設定を書く。認証が必要なプロキシの場合は「-Dhttp.prxyUser=」「-Dhttp.proxyPassword=」も設定する。
  4. DartEditor.ini ファイルを保存する。

設定すると DartEditor.ini は次のようになります。
(前半省略)
-Xms40m
-Xmx1000m
-Dhttp.proxyHost=proxy.example.com
-Dhttp.proxyPort=8080
-Dhttp.proxyUser=account_name
-Dhttp.proxyPassword=account_password
Dart Editor 0.6.21_r26639 と、Windows XP および Ubuntu 13.04 の環境で確認しました。

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

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) へ続く