我想要合并两个网络信号,但有一些限制。
让我们调用网络信号A和B.A确实使用AFNetworking在缓存中查找资源并立即返回该请求的任何响应。 B也会考虑缓存,但可以转到远程服务器以重新验证响应。
好的,我想做的是:
请求A:
请求B:
我目前的解决方案是:
- (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;
}
我知道这个解决方案不符合我的要求,所以我想知道是否有人可以帮助我找到更好的解决方案。
- (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
]];
}
答案 0 :(得分:12)
这是一个很难回答的问题,因为您所需的错误处理从根本上与RACSignal
API合同不符,后者声明errors have exception semantics。
忽略但仍然关心错误的唯一方法是将它们重定向到其他位置。在这个例子中,我将使用一个主题:
RACSubject *remoteErrors = [RACSubject subject];
...但您也可以使用属性或其他一些通知机制。
我会继续使用上面给出的remoteSignal
和cacheSignal
进行一些修改。这是我们想要的行为:
remoteSignal
应将错误发送至remoteErrors
cacheSignal
应在remoteSignal
发送值后立即取消
cacheSignal
和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:
上使用cacheSignal
可确保remoteSignal
之后无法发送值。
再看一下需求列表,您可以看到用于实现每个需求的运算符:
[-doError:]
remoteSignal
应将错误发送至remoteErrors
[-takeUntil:]
cacheSignal
应在remoteSignal
发送值后立即取消
[-catchTo:]
来自任何一个信号的错误都不应该终止其他[+merge:]
我们希望合并cacheSignal
和remoteSignal
中的值,以便在读取缓存后仍然获取远程值