【问题标题】:Reactive Extensions: How to create a placeholder observable?反应式扩展:如何创建可观察的占位符?
【发布时间】:2013-07-09 09:43:48
【问题描述】:

我有一个方法,getObs(),它返回一个 observable,应该由所有调用者共享。但是,当有人调用 getObs() 时,该 observable 可能不存在,并且创建它是一个异步操作,所以我的想法是返回一个占位符 observable,一旦创建,它就会被真正的 observable 替换。

我的基本尝试是这样的:

var createSubject = new Rx.Subject();
var placeholder = createSubject.switchLatest();

如果调用 'getObs()' 时真正的 observable 不存在,我可以在哪里返回 placeholder。创建真正的 observable 后,我使用 createSubject.onNext(realObservable),然后将其传递给 switchLatest(),为任何订阅者解包。

但是,为此目的使用 Subject 和 switchLatest 似乎有点矫枉过正,所以我想知道是否有更直接的解决方案?

【问题讨论】:

    标签: system.reactive reactive-programming reactive-extensions-js


    【解决方案1】:

    如果获取 observable 本身的行为是异步的,那么您也应该将其建模为 observable。

    例如...

    var getObsAsync = function () {
        return Rx.Observable.create(function (observer) {
            var token = startSomeAsyncAction(function (result) {
                    // the async action has completed!
                    var obs = Rx.Observable.fromArray(result.dataArray);
                    token = undefined;
                    observer.OnNext(obs);
                    observer.OnCompleted();
                }),
                unsubscribeAction = function () {
                    if (asyncAction) {
                        stopSomeAsyncAction(token);
                    }
                };            
    
            return unsubscribeAction;
        });
    };
    
    var getObs = function () { return getObsAsync().switchLatest(); };
    

    如果您想共享该 observable 的单个实例,但不希望获得 observable直到有人实际订阅,那么您可以这样做:

    // source must be a Connectable Observable (ie the result of Publish or Replay)
    // will connect the observable the first time an observer subscribes
    // If an action is supplied, then it will call the action with a disposable
    // that can be used to disconnect the observable.
    // idea taken from Rxx project
    Rx.Observable.prototype.prime = function (action) {
        var source = this;
        if (!(source instanceof Rx.Observable) || !source.connect) {
            throw new Error("source must be a connectable observable");
        }
    
        var connection = undefined;
        return Rx.Observable.createWithDisposable(function (observer) {
            var subscription = source.subscribe(observer);
    
            if (!connection) {
                // this is the first observer.  Connect the underlying observable.
                connection = source.connect();
                if (action) {
                    // Call action with a disposable that will disconnect and reset our state
                    var disconnect = function() {
                        connection.dispose();
                        connection = undefined;
                    };
                    action(Rx.Disposable.create(disconnect));
                }
            }
    
            return subscription;
        });
    };
    
    var globalObs = Rx.Observable.defer(getObs).publish().prime();
    

    现在在任何地方都可以使用 globalObs 而不必担心它:

    // location 1
    globalObs.subscribe(...);
    
    // location 2
    globalObs.select(...)...subscribe(...);
    

    请注意,实际上没有人需要调用 getObs,因为您只需设置一个全局可观察对象,当有人订阅时,它将(通过 defer)为您调用 getObs

    【讨论】:

    • 一如既往的好答案。你真的很了解你的东西。你能解释一下primerefCount之间的区别吗?
    • refCount - 记录观察者的数量。当计数从 0 变为 1 时,它连接到源。当计数从 1 变为 0 时,它会断开连接。所以,如果你的观察者来来去去,而你的 observable 是 cold,那么如果所有的观察者都取消订阅,它会重置 observable,以便下一个观察者会导致冷的 observable “重新开始”。
    • prime - 当计数从 0 变为 1 时,它连接到源。之后,即使所有观察者都取消订阅,它也永远不会断开连接。断开连接的唯一方法是向prime() 提供函数参数,这样您就可以捕获代表连接的一次性用品。然后你可以随时断开它。
    • 在上面的例子中,如果你使用refCount,那么只要所有的观察者都取消订阅,你从你的异步方法接收到的可观察对象就会被关闭。当一个新的观察者最终订阅时,将对getObs 进行新的调用,并开始向新的观察者共享一个新的 observable。这实际上可能是你想要的。我不知道。使用prime 将确保仅对getObs 进行1 次调用,如果所有观察者都取消订阅一段时间,事件就会“丢失”。
    【解决方案2】:

    您可以在事后使用主题来连接来源:

    var placeholder = new Subject<YourType>();
    // other code can now subscribe to placeholder, best expose it as IObservable
    

    创建源时:

    var asyncCreatedObs = new ...;
    placeholder.Subscribe(asyncCreatedObs);
    // subscribers of placeholder start to see asyncCreatedObs 
    

    【讨论】:

    • 哦,我看到你在 JS... 代码是 C# 的。猜猜JS的语法有点不同,概念应该是一样的。
    猜你喜欢
    • 1970-01-01
    • 2014-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多