【UniRx】 ReactiveCommand/AsyncReactiveCommandについて

# はじめに

UniRxには`ReactiveCommand`と`AsyncReactiveCommand`という機構が用意されています。
こちらは[ICommandインタフェース](https://msdn.microsoft.com/ja-jp/library/system.windows.input.icommand(v=vs.110).aspx)の考えをベースに作られており、「処理を実行できるかどうか」を`IObservable<bool>`を使って許可したり禁止したりできるようにすることができるようになっています。

とりあえず、詳しい使い方を見てみましょう。

# ReactiveCommand

## 概要

`ReactiveCommand`は`IObservable<bool>`を使って、処理の実行の許可/不許可を制御することができます。
サンプルコードを見たほうが早いので、次のコードを御覧ください。

```cs
void Start()
{
    // 実行の許可/不許可を制御するIObservable<bool>
    ReactiveProperty<bool> gate = new BoolReactiveProperty(false);

    // gateを使って実行制御する
    var command = new ReactiveCommand((IObservable<bool>)gate);

    // command.Execute()を実行した時に、
    // 実行ができる状態ならOnNextが発行される
    command.Subscribe(_ =>
    {
        Debug.Log("実行した！");
    });

    // 実行を不許可にする
    gate.Value = false;

    Debug.Log("Execute()を呼び出します gate = false");

    // Executeを実行するとgateの状態を判定し、
    // trueならObservableにOnNextを流す
    // falseなら何もしない
    command.Execute();

    // 実行許可する
    gate.Value = true;

    Debug.Log("Execute()を呼び出します gate = true");

    command.Execute();
}
```

```text
Execute()を呼び出します gate = false
Execute()を呼び出します gate = true
実行した！
```

`ReactiveCommand`は引数として`IObservable<bool>`をとり、この`Observable`の値を用いてコマンドの実行許可/不許可の制御を行います。
`ReactiveCommand`自体が購読可能(`Sunscribe`できる)存在であり、trueが入力されている状態で、`ReactiveCommand`の`Execute()`を実行することで`OnNext`がそこから発行されます。

* イメージ図（falseのとき）

![1.png](https://qiita-image-store.s3.amazonaws.com/0/47146/b3bc9d59-0a8e-75a3-3723-c8547bb85ec7.png)

* イメージ図（trueのとき）

![2_.png](https://qiita-image-store.s3.amazonaws.com/0/47146/d3935fd8-afb7-9686-af18-c2e2390a24cf.png)

## ToReactiveCommand

newしなくても、`IObservable<bool>`から`ToReactiveCommand`で直接生成できます。

```cs
ReactiveProperty<bool> gate = new BoolReactiveProperty(false);
var command = gate.ToReactiveCommand();
```

## 任意のメッセージを発行する場合

`ReactiveCommand<T>`を使う。

```cs
void Start()
{
    ReactiveProperty<bool> gate = new BoolReactiveProperty(false);

    // string型を扱うReactiveCommand
    var command = gate.ToReactiveCommand<string>();

    command.Subscribe(x =>
    {
        Debug.Log(x);
    });

    // 実行を不許可にする
    gate.Value = false;

    command.Execute("ほげ");

    // 実行許可する
    gate.Value = true;

    command.Execute("ふが");
}
```

```text
ふが
```

## UIとの組み合わせ

`ReactiveCommand`は単体でも使えますが、uGUI要素と連携させることでより便利に使えるようになります。
uGUI要素に`Bind`することで、状態に応じてUIのinteractiveも制御してくれるようになります。


```cs
public class ReactiveCommandSample : MonoBehaviour
{
    [SerializeField] private Toggle _toggle;
    [SerializeField] private Button _button;
    [SerializeField] private Text _resultText;

    void Start()
    {
        // toggleの状態を使ったReactiveCommandを作成
        var command = _toggle.OnValueChangedAsObservable().ToReactiveCommand();

        // ButtonにBindする
        command.BindTo(_button);

        command.Subscribe(_ =>
        {
            _resultText.text += "Click!";
        });

        // BindToOnClickでまとめて定義もできる
        // command.BindToOnClick(_button, _ => _resultText.text += "Click!");
    }
}
```

![rt1.gif](https://qiita-image-store.s3.amazonaws.com/0/47146/50f92a29-50e1-060e-f40d-a38bd99edc87.gif)

Toggleの状態に応じてButtonのクリックイベントを実行するかどうかに加え、Buttonそのもののinteractive状態も同時に制御してくれるようになりました。


# AsyncReactiveCommand

`AsyncReactiveCommand`は`ReactiveCommand`を非同期処理に対応させたものになります。

だいたいの挙動は似ているのですが、大きく違う点として`Subscribe`のスキーマが

```cs
IDisposable Subscribe(Func<T, IObservable<Unit>> asyncAction);
```

に変更されており、`Execute()`で呼び出された処理の実行後に`IObservable<Unit>`のreturnが必要になっています。

なぜこのような仕組みになっているのかは、使用例を見ていただくとわかると思います。


## 挙動

引数を指定せずに`AsyncReactiveCommand`を作成した場合は、`Execute()`を実行した際に自分自身で実行状態をfalseに切り替えます。
その上で、購読時に登録した`IObservable<Unit>`の完了(`OnCompleted`/`OnError`状態になる)を待ち受け、全て完了状態になったタイミングでtrueに戻すという仕組みになっています。

文章ではわかりにくいと思うので、実例を見てみましょう。

次のコードは、入力されたURLに対してHTTP通信を行い、結果を表示するという処理です。
その通信処理とButtonの状態を`AsyncReactiveCommand`を使って連結しています。

```cs
public class AsyncReactiveCommandSample : MonoBehaviour
{
    [SerializeField] private InputField _urlText;
    [SerializeField] private Button _button;
    [SerializeField] private Text _resultText;

    void Start()
    {
        var command = new AsyncReactiveCommand();

        command.BindToOnClick(_button, _ =>
        {
            return ObservableWWW.GetWWW(_urlText.text)
                    .ForEachAsync(www => _resultText.text = www.text);
        });
    }
}
```

![rt2.gif](https://qiita-image-store.s3.amazonaws.com/0/47146/0e5c8b9e-c986-e864-40ac-721308cf3022.gif)

少しわかりにくいかもしれませんが、**HTTP通信中はButtonがグレーアウトして無効化されています。**
このように、`AsyncReactiveCommand`を使うことで非同期処理を実行しそれが終了するまで次のコマンドを実行しないといった処理が簡単に書くことができるようになります。つまり、正確な連打防止機構を作ることができるようになります。

ちなみに、１つの`AsyncReactiveCommand`を複数回`Subscribe`して非同期処理を並列で走らせた場合、すべての非同期処理が終わるまで`AsyncReactiveCommand`は有効状態に戻りません。

### 省略記法

この使い方をする場合、わざわざ自分で`AsyncReactiveCommand`をnewするのも冗長です。
UI要素から直接`AsyncReactiveCommand`を生成して利用する省略記法(`BindToOnClick`)が用意されているため、そちらを使ったほうがよいでしょう。

```cs:省略記法
_button.BindToOnClick(_ =>
{
    return ObservableWWW.GetWWW(_urlText.text)
        .ForEachAsync(www => _resultText.text = www.text);
});
```

```cs:比較（自分で定義する書き方)
var command = new AsyncReactiveCommand();

command.BindToOnClick(_button, _ =>
{
    return ObservableWWW.GetWWW(_urlText.text)
            .ForEachAsync(www => _resultText.text = www.text);
});
```

#### 補足: ForEachAsync

`ForEachAsync`は簡単に言ってしまえば`Do() + AsUnitObservable()`です。
非同期に処理を実行したいが、`Subscribe`まではしたくないといった場合に使うことができます。

`ForEachAsync` についての詳細についてはUniRx作者のneueccさんがブログにて紹介されています。

* [同期（風）コードと対比させたUnity+UniRxで非同期を扱う場合のパターン集](http://neue.cc/2015/10/23_518.html)


## `AsyncReactiveCommand`を使ってUIをまとめて制御する

`AsyncReactiveCommand`には`IReactiveProperty<bool>`を引数にとるパターンもあります。
こちらを使うことで、**複数のAsyncReactiveCommandを共通のIReactiveProperty<bool>で制御する**といったことができるようになります。

こちらもコードを見てもらったほうが早いです。

```cs:ボタンが押されたら各々のアクションを実行する
public class AsyncReactiveCommandSample2 : MonoBehaviour
{
    [SerializeField] private Button _wait1SecondButton;
    [SerializeField] private Button _wait2SecondsButton;
    [SerializeField] private Button _httpButton;
    [SerializeField] private Text _resultText;

    /// <summary>
    /// 全ボタンで共通して使うReactiveProperty
    /// </summary>
    private ReactiveProperty<bool> _sharedGate = new ReactiveProperty<bool>(true);

    void Start()
    {

        // 1秒待つボタン
        _wait1SecondButton.BindToOnClick(_sharedGate, _ =>
        {
            _resultText.text = "１秒待つよ";
            return Observable.Timer(TimeSpan.FromSeconds(1))
                .ForEachAsync(__ => _resultText.text = "1秒経ったよ");
        });

        // 2秒待つボタン
        _wait2SecondsButton.BindToOnClick(_sharedGate, _ =>
        {
            _resultText.text = "2秒待つよ";
            return Observable.Timer(TimeSpan.FromSeconds(2))
                .ForEachAsync(__ => _resultText.text = "2秒経ったよ");
        });

        // 通信するボタン
        _httpButton.BindToOnClick(_sharedGate, _ =>
        {
            _resultText.text = "通信するよ";
            return ObservableWWW.GetWWW("https://unity3d.com")
                .ForEachAsync(www => _resultText.text = www.text);
        });
    }
}
```

![rt3.gif](https://qiita-image-store.s3.amazonaws.com/0/47146/3f52d315-e1fb-03a3-f3ec-8da779f4cc14.gif)

このように、`AsyncReactiveCommand`に共通の`IReactiveProperty<bool>`を渡すことで、Buttonをグルーピングしてまとめて状態の管理ができるようになりました。

# まとめ

`ReactiveCommand`の方はちょっと使いみちがあまりない感じがしますが、`AsyncReactiveCommand`の方は汎用性が高くかなり便利に使える気がします。
特にUIにバインドすることで真価を発揮するので、UI周りで困ったら使ってみるとよいのではないでしょうか。

# 参考文献

 * [UniRx 5.4.0 - Unity 5.4対応とまだまだ最適化](http://neue.cc/2016/08/03_536.html)
 * [UniRxでAsyncReactiveCommandを使ったダブルクリックの抑制](https://qiita.com/kuvia/items/e96626e1a4071c39375f)