原因是Error 事件会立即通过运算符链转发,而Error 事件会终止订阅。
解决此问题的一种方法是使用retry 运算符,该运算符在发生错误时重新订阅信号。下面是使用 retry 的示例的修改版本:
@weakify(self);
[[[[[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] flattenMap:^__kindof RACSignal * _Nullable(__kindof UIControl * _Nullable value) {
@strongify(self)
return [self.viewModel signInSignal];
}] flattenMap:^__kindof RACSignal * _Nullable(id _Nullable value) {
return [self.viewModel userInfoSignal];
}] doError:^(NSError * _Nonnull error) {
NSLog(@"Inner Error: %@", error);
}] retry:2]
subscribeNext:^(id _Nullable x) {
NSLog(@"Next: %@", x);
} error:^(NSError * _Nullable error) {
NSLog(@"Error: %@", error);
}];
这里使用了带有retryCount 的变体——它会在将错误传播到外部之前重试两次。此外,doError 用于在发生错误时执行日志调用作为副作用(注意:这必须在retry 运算符之前)
鉴于signInSignal 或userInfoSignal 100% 的时间会产生错误,3 次按钮按下将产生以下输出
内部错误
内部错误
内部错误
错误
RAC 命令
我建议您查看的另一个解决方案是将逻辑封装到 RACCommand(在您的 viewModel 中):
_signInCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
return [self.signInSignal flattenMap:^RACSignal *(id value) {
return self.userInfoSignal;
}];
}];
您可以将其链接到您的按钮,如下所示:
self.button.rac_command = self.viewModel.signInCommand;
这样,你就不需要手动处理重试了:每次点击按钮都会调用一次命令,可以成功完成也可以出错。
您可以通过命令特殊信号处理命令的副作用:
[[self.viewModel.signInCommand errors] subscribeNext:^(NSError * _Nullable x) {
NSLog(@"Error: %@", x);
}];
[self.viewModel.signInCommand.executionSignals subscribeNext:^(id _Nullable signal) {
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"Next: %@", x);
}];
}];
以这种方式使用RACCommand 的一大好处是它会在命令运行时自动禁用按钮,因此如果您的signInSignal 和userInfoSignal 需要一些时间,按钮将同时自动禁用所以用户无法在该操作已经运行时再次启动该操作。