8

我有两个要合并的网络信号,但有一些限制。

让我们将网络信号称为 A 和 B。A 确实使用 AFNetworking 在缓存中查找资源并立即返回对该请求的任何响应。B 也考虑缓存,但可以到远程服务器重新验证响应。

好的,所以我想做的是:

要求一:

  • 应该尽快做 sendNext。
  • 如果 B 已经完成了 sendNext,我们将忽略 A。
  • 如果出现问题,并且 A 产生错误,我们应该忽略它。

请求 B:

  • 应该尽快做 sendNext,即使 A 已经做了一个 sendNext。
  • 如果出现问题,我对 B 的错误很感兴趣,但它不应该阻止 A。

我目前的解决方案是这样的:

- (RACSignal *)issueById:(NSString *)issueId {

    RACSignal *filterSignal = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
        RACSignal *cacheSignal = [[IssueWSRequest instance] issueWithId:issueId cachePolicy:NSURLRequestReturnCacheDataDontLoad];

        return [cacheSignal subscribeNext:^(id x) {
            [subscriber sendNext:x];
        } error:^(NSError *error) {
            NSLog(@"Ignore error");
            [subscriber sendCompleted];
        } completed:^{
            [subscriber sendCompleted];
        }];
    }];

    RACSignal *remoteSignal = [[IssueWSRequest instance] issueWithId:issueId cachePolicy:NSURLRequestUseProtocolCachePolicy];

    RACSignal *combined = [RACSignal merge:@[newSign, remoteSignal]];
    return combined;
}

我知道这个解决方案不能满足我的要求,所以我想知道是否有人可以帮助我提供更好的解决方案。

我的解决方案(来自@JustinSpahr-Summers 的回答):

- (RACSignal *)issueById:(NSString *)issueId {

    RACSubject *localErrors = [RACSubject subject];

    RACSignal *remoteSignal = [[IssueWSRequest instance] issueWithId:issueId cachePolicy:NSURLRequestUseProtocolCachePolicy];

    RACSignal *cacheSignal = [[[[[[IssueWSRequest instance] issueWithId:issueId cachePolicy:NSURLRequestReturnCacheDataDontLoad] 
            takeUntil:remoteSignal] doError:^(NSError *error) {
                [localErrors sendNext:error];
            }] finally:^{
                // Make sure to complete the subject, since infinite signals are
                // difficult to use.
                [localErrors sendCompleted];
            }]
            replayLazily];

    return [RACSignal merge:@[
            [cacheSignal catchTo:[RACSignal empty]],
            remoteSignal
    ]];
}
4

1 回答 1

12

这是一个很难回答的问题,因为您想要的错误处理从根本上与RACSignalAPI 合同不兼容,后者声明错误具有异常语义

忽略但仍然关心错误的唯一方法是将它们重定向到其他地方。在这个例子中,我将使用一个主题:

RACSubject *remoteErrors = [RACSubject subject];

…但您也可以使用属性或其他一些通知机制。

我将继续使用您在上面给出的,并进行一些修改remoteSignalcacheSignal这是我们希望他们的行为:

  1. remoteSignal应该将其错误发送到remoteErrors
  2. cacheSignal应在remoteSignal发送值后立即取消
  3. 来自任一信号的错误不应终止另一个
  4. 我们想合并来自cacheSignaland的值remoteSignal,这样在缓存被读取后我们仍然可以得到远程值

考虑到这一点,让我们看一下remoteSignal

RACSignal *remoteSignal = [[[[[IssueWSRequest
    instance]
    issueWithId:issueId cachePolicy:NSURLRequestUseProtocolCachePolicy]
    doError:^(NSError *error) {
        [remoteErrors sendNext:error];
    }]
    finally:^{
        // Make sure to complete the subject, since infinite signals are
        // difficult to use.
        [remoteErrors sendCompleted];
    }]
    replayLazily];

-doError:-finally:控制remoteErrors主题,满足我们上面的第一个要求。因为我们需要remoteSignal在多个地方使用(正如您可以在上面的列表中看到的那样),我们使用-replayLazily以确保它的副作用只发生一次。

cacheSignal几乎没有变化。我们只需要使用-takeUntil:它来确保它在remoteSignal发送值时终止(但如果它发送错误则不会):

RACSignal *cacheSignal = [[[IssueWSRequest
    instance]
    issueWithId:issueId cachePolicy:NSURLRequestReturnCacheDataDontLoad]
    takeUntil:remoteSignal];

最后,我们要合并它们的值,以便两个信号同时启动,并且它们的值可以以任意顺序到达:

return [RACSignal merge:@[
    [cacheSignal catchTo:[RACSignal empty]],
    [remoteSignal catchTo:[RACSignal empty]]
];

我们在这里忽略错误,因为任何一个错误都会终止两者(因为它们现在已经合并)。上面已经处理了我们的错误处理行为。

而且,尽管合并,使用-takeUntil:oncacheSignal确保它不可能在之后发送值remoteSignal

再看一下需求列表,您可以看到用于满足每个需求的运算符:

  1. [-doError:] remoteSignal应该将其错误发送到remoteErrors
  2. [-takeUntil:] cacheSignal应在remoteSignal发送值后立即取消
  3. [-catchTo:]来自任一信号的错误不应终止另一个
  4. [+merge:]我们想合并来自cacheSignaland的值remoteSignal,这样在缓存被读取后我们仍然可以得到远程值
于 2013-12-06T18:53:32.550 回答