【UniRx】Observableが完了時に処理を行うオペレータ #103
前回の成果
エラーハンドリングのオペレータをまとめた。
今回やること
Observableが完了時に処理を行うオペレータをまとめます。
- 前回の成果
- 今回やること
- ストリームのOnCompletedが呼ばれたら、同じストリームを生成する
- 同じストリームを指定回数生成する
- 短時間にOnCompletedが呼ばれた場合、Repeatを止める
- ストリームのOnCompletedが呼ばれたら、同じストリームを生成し、指定したGameObjectが非表示になったら、Repeatを中止する
- ストリームのOnCompletedが呼ばれたら、同じストリームを生成し、指定したGameObjectが破棄されたら、Repeatを中止する
- ストリームが完了時、例外発生時、破棄時に処理を行う
- 参考サイト様
ストリームのOnCompletedが呼ばれたら、同じストリームを生成する
Repeat
OnCompletedが呼ばれた際に、同じストリームを生成してSubscribeします。
ただし、このオペレータは無限ループが発生するので注意してください。
可能ならば、下記で紹介するRepeatUntilDisableやRepeatUntilDestoryを使用した方が良いケースが多いです。
ソースコード
/// <summary> /// ストリームのOnCompletedが呼ばれたら、同じストリームを生成する /// </summary> private void ExcuteRepeat() { Observable .Timer(TimeSpan.FromSeconds(1)) .Do(_ => Debug.Log("Do")) .DoOnCompleted(() => Debug.Log("DoOnCompleted")) .Repeat() .Subscribe(); }
結果
Timerで1秒後にメッセージを発行し、OnCompletedが発行されています。
OnCompletedが発行されたので、再びSubscribeをしています。
こちらの例では、無限ループしています。
同じストリームを指定回数生成する
Repeat
Repeatの第一引数に発行する値を、第二引数に回数を指定することが出来ます。
定義は以下のようになっています。
/// <summary> /// 回数指定のRepeat /// </summary> /// <param name="value">発行する値</param> /// <param name="repeatCount">Repeatの回数</param> public static IObservable<T> Repeat<T>(T value, int repeatCount) { return Repeat(value, repeatCount, Scheduler.DefaultSchedulers.Iteration); }
ソースコード
/// <summary> /// 同じストリームを指定回数生成する /// </summary> private void ExcuteRepeatCount() { Observable .Repeat("Repeat", 3) .Subscribe(x => { Debug.Log("OnNext : " + x); }, () => { // OnCompletedは1回しか呼ばれない Debug.Log("OnCompleted"); }); }
結果
OnNextが第二引数で指定した回数呼ばれ、その後OnCompletedが呼ばれています。
引数無しのRepeatと異なり、OnCompletedは一度しか呼ばれません。
短時間にOnCompletedが呼ばれた場合、Repeatを止める
RepeatSafe
OnCompletedが短時間で呼ばれるRepeatは無限ループが挙げられます。
そのようなケースが発生した場合に、このオペレータを使用することで防止することができます。
ただし、意図的に制御することができません。
ですので、以下で紹介するRepeatUntilDisable やRepeatUntilDestoryを使用するのをおすすめします。
ソースコード
/// <summary> /// 短時間にOnCompletedが呼ばれた場合、Repeatを止める /// 意図的に制御できない /// </summary> private void ExcuteRepeatSafe() { Subject<Unit> subject = new Subject<Unit>(); subject .Do(_ => { Debug.Log("Do"); }) .DoOnCompleted(() => { Debug.Log("DoOnCompleted"); }) // Repeatだと無限ループしてしまう //.Repeat() .RepeatSafe() .Subscribe(x => { Debug.Log("OnNext : " + x); }, () => { Debug.Log("OnCompleted"); }); subject.OnCompleted(); }
結果
Repeatですと無限ループしてしまうのを防ぐことができます。
ストリームのOnCompletedが呼ばれたら、同じストリームを生成し、指定したGameObjectが非表示になったら、Repeatを中止する
RepeatUntilDisable
引数で指定したGameObjectが非表示になった際に、Repeatを中止することができます。
RepeatSafeと違い、Repeatを中止するタイミングを制御できます。
ソースコード
/// <summary> /// ストリームのOnCompletedが呼ばれたら、同じストリームを生成する /// 指定したGameObjectが非表示になったら、Repeatを中止する /// </summary> private void ExcuteRepeatUntilDisable() { this.UpdateAsObservable() .Where(_ => Input.anyKeyDown) .Subscribe(_ => { Debug.Log("anyKeyDown"); gameObject.SetActive(false); }); Observable .Timer(TimeSpan.FromSeconds(1)) .Do(_ => Debug.Log("Do")) .RepeatUntilDisable(gameObject) .Subscribe(x => { Debug.Log("OnNext : " + x); }, () => { Debug.Log("OnCompleted"); }); }
結果
指定したGameObject(今回の場合、ObservableCompleted)が非表示になった場合にRepeatが中止されています。
ストリームのOnCompletedが呼ばれたら、同じストリームを生成し、指定したGameObjectが破棄されたら、Repeatを中止する
RepeatUntilDestroy
引数で指定したGameObjectが破棄された際に、Repeatを中止することができます。 RepeatSafeと違い、Repeatを中止するタイミングを制御できます。
ソースコード
/// <summary> /// ストリームのOnCompletedが呼ばれたら、同じストリームを生成する /// 指定したGameObjectが破棄されたら、Repeatを中止する /// </summary> private void ExcuteRepeatUntilDestory() { this.UpdateAsObservable() .Where(_ => Input.anyKeyDown) .Subscribe(_ => { Debug.Log("anyKeyDown"); Destroy(gameObject); }); Observable .Timer(TimeSpan.FromSeconds(1)) .Do(_ => Debug.Log("Do")) .RepeatUntilDestroy(gameObject) .Subscribe(x => { Debug.Log("OnNext : " + x); }, () => { Debug.Log("OnCompleted"); }); }
結果
指定したGameObject(今回の場合、ObservableCompleted)が破棄された場合にRepeatが中止されています。
ストリームが完了時、例外発生時、破棄時に処理を行う
DoOnTerminate / Finally
ストリームのOnCompleted、OnError、Dispose時に処理を行うことができます。
DoOnTerminateとFinallyの違いは以下になります。
\ | 完了時 | 例外時 | 破棄時 |
---|---|---|---|
DoOnTerminate | 呼ばれる | 呼ばれる | 呼ばれない |
Finally | 呼ばれる | 呼ばれる | 呼ばれる |
ソースコード
/// <summary> /// ストリーム完了時のDoOnTerminateとFinallyの違い /// </summary> private void ExcuteDiffCompleted() { // DoOnTerminateとFinallyの両方が呼ばれる CreateSubject().OnCompleted(); } /// <summary> /// 例外発生時のDoOnTerminateとFinallyの違い /// </summary> private void ExcuteDiffError() { // DoOnTerminateとFinallyの両方が呼ばれる CreateSubject().OnError(new Exception()); } /// <summary> /// ストリーム破棄時のDoOnTerminateとFinallyの違い /// </summary> private void ExcuteDiffDispose() { // Finallyのみ呼ばれる CreateDisposable().Dispose(); } /// <summary> /// DoOnTerminateとFinallyの違いのSubject生成 /// </summary> private Subject<Unit> CreateSubject() { Subject<Unit> subject = new Subject<Unit>(); subject // 例外発生時 .DoOnError (e => Debug.Log("DoOnError")) // ストリーム完了時 .DoOnCompleted(() => Debug.Log("DoOnCompleted")) // ストリーム破棄時 .DoOnCancel (() => Debug.Log("DoOnCancel")) // ストリーム完了、例外発生時 .DoOnTerminate(() => Debug.Log("DoOnTerminate")) // ストリーム完了、ストリーム破棄、例外発生時 .Finally (() => Debug.Log("Finally")).Subscribe(); return subject; } /// <summary> /// DoOnTerminateとFinallyの違いのDisposable生成 /// </summary> private IDisposable CreateDisposable() { Subject<Unit> subject = new Subject<Unit>(); return subject // 例外発生時 .DoOnError (e => Debug.Log("DoOnError")) // ストリーム完了時 .DoOnCompleted(() => Debug.Log("DoOnCompleted")) // ストリーム破棄時 .DoOnCancel (() => Debug.Log("DoOnCancel")) // ストリーム完了、例外発生時 .DoOnTerminate(() => Debug.Log("DoOnTerminate")) // ストリーム完了、ストリーム破棄、例外発生時 .Finally (() => Debug.Log("Finally")).Subscribe(); }
ストリーム完了時の結果
DoOnTerminateもFinallyも呼ばれています。
ストリーム例外発生時の結果
DoOnTerminateもFinallyも呼ばれています。
ストリーム破棄時の結果
Finallyのみ呼ばれています。
今回は以上となります。
ここまでご視聴ありがとうございました。