1

演習として、互いに更新する 2 つの依存ストリームを構築しようとしています。

テスト アプリケーションは単純な "インチ <-> センチメートル" コンバーターであり、両方の入力が編集可能です。

私が経験している問題は、1 つのフィールドの変更を引き起こす再帰を停止する方法がわからないことです。

問題をよりよく説明するために、コードの関連部分を見てみましょう。

var cmValue = new Rx.BehaviorSubject(0),
    inValue = new Rx.BehaviorSubject(0);

# handler #1
cmValue.distinctUntilChanged().subscribe(function(v) {
    inValue.onNext(cmToIn(v));
});

# handler #2
inValue.distinctUntilChanged().subscribe(function (v) {
    cmValue.onNext(inToCm(v));
});

そのため、それぞれが現在の対応する値を保持するサブジェクトを定義します。

ここで、インチ単位の値を2(キーボードを使用inValue.onNext(2);またはキーボード経由で) に変更したとします。

次に何が起こるか - ハンドラー #2 がトリガーされ、対応するセンチメートル単位の値の再計算が呼び出されます。どの結果にcmValue.onNext(0.7874015748031495).

実際、この呼び出しはハンドラー #1 によって処理され、0.7874015748031495 * 2.54別の呼び出しを引き起こす数式を使用して、インチ単位の値 (手動で入力したもの) が再計算されinValue.onNext(1.99999999999999973)ます。

幸いなことに、FP 丸め誤差のため、ここで停止します。しかし、他のシナリオでは、これによりさらにループが発生したり、無限再帰が発生したりする可能性があります。

ご覧のとおり、問題を部分的に解決しました.distinctUntilChanged()。これにより、少なくとも変更時の無限再帰から保護されますが、この場合、値が同一ではないため、問題を完全に解決することはできません (FP 操作のため)。自然)。

問題は、自己再帰をまったく引き起こさない一般的な双方向バインディングをどのように実装するかということです。

丸めを使用.select()することは、この特定の問題の部分的な解決策であり、一般的なものではありません (私と他のすべての人が好む)。

完全なコードとデモ: http://jsfiddle.net/ewr67eLr/

4

4 に答える 4

1

私のコメントで述べたように、この問題にはループは必要ありません。また、サブジェクトや document.activeElement も必要ありません。入力 A からのイベントで B を更新し、入力 B からのイベントで A を更新し、ストリームが相互に参照することはありません。

例:

http://jsfiddle.net/kcv15h6p/1/

ここに関連するビット:

var cmElement = document.getElementById('cm'),
    inElement = document.getElementById('in'),
    cmInputValue = Rx.Observable.fromEvent(cmElement, 'input').map(evToValue).startWith(0),
    inInputValue = Rx.Observable.fromEvent(inElement, 'input').map(evToValue).startWith(0);


inInputValue.map(inToCm).subscribe(function (v) {
    cmElement.value = myRound(v, 3).toFixed(3);
});

cmInputValue.map(cmToIn).subscribe(function (v) {
    inElement.value = myRound(v, 3).toFixed(3);
});

本当にループが必要な問題については、この質問に対する Brandon の回答で指摘されているように、defer を使用してループを作成できます。

オブザーバブル間の循環依存をキャッチ

他のループ構造と同様に、無限ループを避けるために終了条件を処理する必要があります。これは、take()、distinctUntilChanged() などの演算子を使用して行うことができます。後者はコンパレータを使用することに注意してください。たとえば、ループを終了するには、オブジェクト ID (x, y) => x === y を使用できます。

于 2014-12-24T20:29:29.930 に答える
1

デモには、2 つの入力フィールドがあります。この入力の "Keyup" イベントが情報ソースになり、入力値が宛先になります。この場合、オブザーバブルの更新をチェックするために可変状態は必要ありません。

Rx.Observable.fromEvent(cmElement, 'keyup')
    .map(targetValue)
    .distinctUntilChanged()
    .map(cmToIn)
    .startWith(0)
    .subscribe(function(v){ inElement.value = v; });

Rx.Observable.fromEvent(inElement, 'keyup')
    .map(targetValue)
    .distinctUntilChanged()
    .map(inToCm)
    .startWith(0)
    .subscribe(function(v){ cmElement.value = v; });

ここで私の例を確認してください: http://jsfiddle.net/537Lrcot/2/

于 2014-12-23T07:47:05.060 に答える