首页 > 解决方案 > ReactiveSwift 链接的 SignalProducers 不会将完成发送到下游

问题描述

因为我是响应式编程的新手,所以我对在 ReactiveSwift 中链接 SignalProducers 有一些初学者的问题。我的目标是创建一个 SignalProducers 链,以测试我的一个流程。在进行 SignalProducer 链接时,我偶然发现了一个障碍。如果我最里面的 SignalProducer 正在调用 sendCompleted 它不会被传播到下游。这是 Playground 中的模拟代码

  private func doSomethingWithString() -> SignalProducer<Int, Error> {
  return SignalProducer<String, Error> {observer, _ in
    observer.send(value: "Hello")
  }.flatMap(.latest, doSomethingWithInt(string:))
}

private func doSomethingWithInt(string: String) -> SignalProducer<Int, Error> {
  return SignalProducer<Int, Error> { observer, _ in
    observer.send(value: 2)
    observer.sendCompleted()
  }
}

func test() -> SignalProducer<Int,Error> {
  return doSomethingWithString()

}
func leGo() {

  test().start { (event) in
    switch event {
    case .completed:
      print("DONE")
    case .value(let value):
      print("WE HAVE A VALUE: \(value)")
    case.interrupted:
      print("INTERRUPTED")
    case .failed(let error):
      print("FAILED \(error)")
    }
  }
}

leGo()

在此代码段中,值按预期打印“WE HAVE A VALUE 2”,但从不执行完成,因此从不打印“DONE”。如果有人解释为什么会这样以及如何正确地做到这一点,我将不胜感激。sendCompleted()我可以通过调用in使其工作doSomethingWithString,但由于我的原始流有两个以上的方法,我不想在每个方法中都写它。这.take(first:1)也是一个对我来说听起来很奇怪的选项,因为我真的不想通过所有的链条来拿 1 件物品。相反,如果可能的话,我想在一个完成的地方终止整个流。

标签: swiftreactive-swift

解决方案


的目的flatMap是将每个传入的值转换为新的生产者,然后根据某种策略将来自这些内部生产者的值扁平化为单个流。当内部生产者完成时,它不会发送已完成事件,原因很简单,您可以拥有多个内部生产者,并且如果它在其内部生产者之一完成时完成,您将错过所有其他内部生产者的值。

所以答案是,如果你希望整个流完成,你的外部生产者必须发送一个完成的事件。

这是一个flatMap常用的示例,希望能说明为什么会这样flatMap工作:

let searchResults = searchText
    .flatMap(.latest) { text in
        performSearch(for: text)
    }

searchResults每次用户更改搜索文本时,该信号都会使用新结果进行更新。因此,您希望仅因为其中一项搜索完成而完成流;您想在用户更改文本时启动新搜索并更新结果。

您可以编写某种辅助初始化程序SignalProducer,用于take(first: 1)在单个值之后自动完成,但是让所有生产者在完成后正确发送完成事件更为惯用。


推荐阅读