【问题标题】:How can I subscribe to the completion of a command's execution signals without a nested subscription?如何在没有嵌套订阅的情况下订阅命令执行信号的完成?
【发布时间】:2014-04-17 11:58:44
【问题描述】:

我尝试了以下但没有成功。使用 -subscribeNext: 的等效项按预期工作。

// A
[[_viewModel.loginCommand.executionSignals flatten] subscribeCompleted:^{
    NSLog(@"A");
}];

我唯一的工作实现如下:

// B
[_viewModel.loginCommand.executionSignals subscribeNext:^(RACSignal *loginSignal) {
    [loginSignal subscribeCompleted:^{
        NSLog(@"B");
    }];
}];

为什么-flatten 不能在“A”中工作,我该如何重写“B”以不使用嵌套订阅?

【问题讨论】:

    标签: objective-c reactive-cocoa frp


    【解决方案1】:

    我只是想,从技术上讲,成功完成只是将执行状态更改为 NO,-executionSingals 至少发送了一次值并且在上次执行状态更改为 YES 后没有错误。

    基于这样的想法我做了一个分类:

    #import "RACCommand+ARLCompletedSignal.h"
    
    @implementation RACCommand (ARLCompletedSignal)
    
    - (RACSignal *)completed
    {
        RACSignal *executing = self.executing;
        RACSignal *signals = self.executionSignals;
        RACSignal *errors = self.errors;
    
        RACSignal *startingExecution = [RACSignal combineLatest:@[executing, [signals take:1]]
                                                         reduce:^id(NSNumber *executing, id _){ return executing; }];
    
        return [[startingExecution
           ignore:@NO]
           flattenMap:^RACStream *(id value) {
               RACSignal *comletedOrFailed = [[executing ignore:@YES] subscribeOn:[RACScheduler scheduler]];
               return [[[comletedOrFailed take:1] takeUntil:errors] map:^id(id value) { return nil; }];
           }];
    }
    
    @end
    

    标题:

    @interface RACCommand (ARLCompletedSignal)
    
    @property (nonatomic, readonly) RACSignal *completed;
    
    @end
    

    这里-comleted 在命令成功完成其操作时发送 nil。 也可以在https://gist.github.com/slabko/546de430a16994a5da8e 找到版本,如果操作成功完成则发送 YES,否则发送 NO。

    我在一些非常简单的案例中进行了尝试,并且成功了。请告诉我,如果它不适合你的。

    但是,我相信,在大多数情况下,在将原始信号传递给命令之前订阅原始信号的完成是最好的“无黑客”选项。

    【讨论】:

      【解决方案2】:

      -flatten 运算符返回一个仅在所有内部信号都完成时才完成的信号,这需要外部信号也完成。 -concat 也是如此。因此,一旦应用任一运算符,生成的信号就没有单独完成的表示,只有最终的聚合完成。

      作为嵌套订阅的替代方案,您可以转换内部信号,以便它们发送一个表示完成的值。一种方法是使用-materialize:

      [[[_viewModel.loginCommand.executionSignals
          map:^(RACSignal *loginSignal) {
              // Using -ignoreValues ensures only the completion event is sent.
              return [[loginSignal ignoreValues] materialize];
          }]
          concat]
          subscribeNext:^(RACEvent *event) {
              NSLog(@"Completed: %@", event);
          }];
      

      请注意,我使用了-concat 而不是-flatten,因为它与RACCommand 的默认串行执行的语义相匹配。他们最终在这种情况下做同样的事情,-flatten 退化为-concat 的行为,因为该命令一次只执行一个信号。

      使用-materialize 不是唯一的方法,它只是发送一个表示完成的值,但这可能是您认为对您的用例具有适当意义的任何值。

      【讨论】:

      • 这是很常见的操作 - 显示操作成功的确认。例如,我想发布一条消息,我希望看到操作是失败还是完成。真的没有办法更清楚地做到这一点吗?我的意思是,这种方法仍然只是对嵌套执行信号的订阅。例如,如果命令有多个执行信号怎么办?
      • RACCommandexecutionSignals 不直接公开完成事件。如果您不想使用materialize,另一种选择是使用一个有意义的值,该值被记录为表示“成功完成”。对于不发送任何值的信号,您可以发送任何值来指示完成,然后忽略它。是的,另一种选择是使用 doCompleted: 运算符作为嵌套订阅的替代方案。
      • 所以基本上 RACCommand 不是用于跟踪操作是失败还是成功,它只是跟踪操作何时执行以及何时不执行。我猜对了吗?
      • 不完全。您可以跟踪错误,可以跟踪值/结果,甚至可以跟踪完成情况,但不幸的是只能间接/非惯用方式。它的典型/主要意图是防止某种并发操作,而不是一次只让某件事发生。为了使其更强大,它公开了一个指示操作是否可以开始的信号。所有这些都对 UI 很有帮助。
      猜你喜欢
      • 2021-11-25
      • 1970-01-01
      • 1970-01-01
      • 2020-10-17
      • 2016-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-02
      相关资源
      最近更新 更多