捕获可观察对象之间的循环依赖关系

时间:2013-06-29 21:30:39

标签: reactive-programming rxjs reactive-extensions-js

我有一个用户编程场景,用户最终可以创建两个相互依赖的observable。 RxJS不允许循环依赖,据我所知,内存或堆栈达到了极限,onError回调是用值true触发的。

如何显式检测循环依赖项并抛出更具描述性的错误消息?

此代码说明了如何在RxJS中创建循环依赖:

var obsA,
    obsB;

obsA = Rx.Observable
    .returnValue(42)
    .combineLatest(obsB, function (a, b) {
        return a + b;
    });

obsB = Rx.Observable
    .returnValue(42)
    .combineLatest(obsA, function (b, a) {
        return b + a;
    });


obsA
    .subscribe(function (val) {
        console.log('onNext:' + val);
    },
    function (err) {
        console.error('onError: ' + err);
    },
    function () {
        console.log('onCompleted');
    });

错误消息只是true

1 个答案:

答案 0 :(得分:2)

原始问题中的代码不会创建循环依赖项。在您定义ObsA时,ObsBundefined,所以您真正做的就是致电combineLatest(undefined, function ...)。因此,您看到的错误是因为您将undefined传递给combinedLatest()

实际上需要花费一些精力来创建一个真正的循环依赖。如果你使用defer,那么你将拥有一个真正的循环依赖:

var obsA,
    obsB,
    aRef,
    bRef;

aRef = Rx.Observable.defer(function () {
    return obsA;
});

bRef = Rx.Observable.defer(function () {
    return obsB;
});

obsA = Rx.Observable
    .returnValue(42)
    .combineLatest(bRef, function (a, b) {
            return a + b;
    });

obsB = Rx.Observable
    .returnValue(42)
    .combineLatest(aRef, function (b, a) {
        return b + a;
    });

obsA.subscribe();
<script src='https://rawgit.com/Reactive-Extensions/RxJS/v.2.5.3/dist/rx.all.js'></script>

现在这是一个真正的循环依赖。不幸的是,你仍然会得到相同的错误,尽管有更深的堆栈跟踪:

RangeError: Maximum call stack size exceeded.
/* ... stack ... */

没有万无一失的方法来检测周期。您可以将observable包装在一个新的observable中,并检测对subscribe方法的递归调用。但是,如果基础可观察量使用subscribeOnpublishconcat延迟实际循环订阅的任何其他内容,则此算法将被取消。

我最好的建议是附加一个catch子句来检查范围错误,并用更好的错误替换它:

var obsA,
    obsB,
    aRef,
    bRef;

aRef = Rx.Observable.defer(function () {
    return obsA;
});

bRef = Rx.Observable.defer(function () {
    return obsB;
});

obsA = Rx.Observable
    .returnValue(42)
    .combineLatest(bRef, function (a, b) {
            return a + b;
    })
    .catch(function (e) {
        var isStackError = e instanceof RangeError && e.message === 'Maximum call stack size exceeded';
    
        return Rx.Observable.throw(isStackError ? new Error('Invalid, possibly circular observables.') : e);
    });

obsB = Rx.Observable
    .returnValue(42)
    .combineLatest(aRef, function (b, a) {
        return b + a;
    })
    .catch(function (e) {
        var isStackError = e instanceof RangeError && e.message === 'Maximum call stack size exceeded';
    
        return Rx.Observable.throw(isStackError ? new Error('Invalid, possibly circular observables.') : e);
    });

obsA.subscribe();
<script src='https://rawgit.com/Reactive-Extensions/RxJS/v.2.5.3/dist/rx.all.js'></script>