ReactiveCocoa - 订阅第二个信号,如果第一个信号没有值完成

ReactiveCocoa - Subscribe to the second signal, if the first completes with no value

假设我有两个信号:一个便宜的和一个昂贵的:

RACSignal *localSignal;      // Cheap signal. Sends object without network request 
                             // if possible, otherwise completes immediately. 

RACSingal *networkSignal;    // Expensive one. Always sends data, 
                             // but requires expensive network operation.

现在我想创建一个信号,它从第一个信号(如果有)发送值,或者订阅第二个信号并从该信号发送数据。

下面的解决方案几乎可以满足我的需求,但它总是订阅第二个昂贵的信号,即使第一个信号的值被采用,第二个信号的值被忽略。

[[localDataSignal concat:networkDataSignal] take:1];

有没有高效解决问题的方法?

您可以使用:

- (RACSignal *)catchTo:(RACSignal *)signal;

像这样:

[[localSignal catchTo:networkSignal] subscribeNext:^(id x) {
    // If localSignal sends error, networkSignal will be subscribed.
}];

我刚刚发现问题所在。本地信号使用创建和调用它的同一线程:

RACSignal *localSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    NSLog(@"Perfoming local operation");
    [subscriber sendNext:@"local value"];
    [subscriber sendCompleted];
    return nil;
}];  

正如我看到的问题(我不确定 100%)take:1 甚至没有机会处理连接的网络信号。但是,如果我在不同于主线程调度程序的地方安排本地信号,那么 take:1 会按预期工作——它会在获得第一个值后中断链。

简单来说,这段代码有效:

[[[localSignal deliverOn:[RACScheduler scheduler]] concat:networkSignal] take:1]

它给了我我想要的东西:本地信号的值,如果有的话,或者订阅网络信号并给我它的值。如果本地信号发送值,网络信号永远不会被订阅。

经验法则是检查信号工作的线程,然后 take:1 应该按预期工作。

更新: -deliveOnMainThread 也给了 -take: 处理队列的机会。

有点难看,但是在网络信号之前,concat一个有延迟的空信号。我没试过这个,但它应该引入允许 take 防止在 localSignal 发送值的情况下订阅 networkSignal 所需的异步。此方法不会影响 localSignal.

值的传递
[[RACSignal concat:@[
    localSignal,
    [[RACSignal empty] deliverOn:RACScheduler.mainThreadScheduler],
    networkSignal
] take:1]

作为 deliverOn: 的替代方案,您可以考虑 delay: 如果这样可以使这段代码更容易理解。