【问题标题】:Combine's Future never completes when flatMapped当 flatMapped 时,Combine 的 Future 永远不会完成
【发布时间】:2020-01-22 07:56:32
【问题描述】:

我有以下简单的Future

class ViewModel {
    var cancellables = Set<AnyCancellable>()
    func test() {
        let trigger = PassthroughSubject<Void, Error>()

       let future =  Future<String, Error> { promise in
                       promise(.success("Future Succeded"))
                   }

        trigger
        .flatMap { future }
        .sink(receiveCompletion: { completion in
            print("completion received \(completion)")
        }, receiveValue: { val in
            print("value received \(val)")
        })
        .store(in: &cancellables)

        trigger.send(())
    }
}

我不知道为什么在与另一个发布者(在本例中为 PassthroughSubject)进行平面映射时它永远不会完成,它只会产生值。

当它不是平面映射时,它会产生值并正常完成。

【问题讨论】:

  • 当我将 flatMap 更改为 map 并且不进行任何进一步更改时,也不会收到完成。

标签: ios combine


【解决方案1】:

这种行为可能看起来很奇怪,但很有意义。完成Future 不会完成PassthroughSubject。因此,您可以继续通过PassthroughSubject 发送值,这将导致创建并触发新的Future 实例。通常,Publisher 只能完成或出错一次。因此,如果完成 Future 会触发 sink 完成关闭,这意味着 PassthroughSubject 不能再产生新值,这是不可取的,因为 PassthroughSubject 通常永远不会完成(除非你直接告诉它)。

与您的示例类似,此代码也只触发一次完成:

var cancellables = Set<AnyCancellable>()

(0..<2).publisher
  .flatMap { _ in return (0..<5).publisher }
  .sink(receiveCompletion: { completion in
    print("completion received \(completion)")
  }, receiveValue: { val in
    print("value received \(val)")
  })
  .store(in: &cancellables)

原因是创建的发布者会发布两个值,然后完成。如果 flatMap 发布者会导致调用 sink 完成,这意味着 (0..&lt;2) 发布者完成,但它仍有值要发送,所以它没有完成。

长话短说,开始发布者决定流何时完成;不是平面图发布者。

【讨论】:

  • 很好的解释。请注意,您可以通过添加 trigger.send(completion: .finished) 来完成问题示例。
  • 是的,我的印象是.flatMap 制作的发布者将其.finished 完成“吞下”,因此它不会通过下游。这是有道理的,因为正如您所说,通过完成来终止管道的操作不是生产发布者的工作。 — 另一方面,如果生产的发布者抛出错误,管道确实终止。
【解决方案2】:

我以为我遇到了这个问题,但事实证明这只是因为我没有保留发布者的返回值。如sink() 的文档中所述

应该保留返回值,否则会取消流。

我的游乐场代码:

var t:Timer?
print(Date())
let _ = Just(33)
    .flatMap { (i) -> Future<Int, Never> in
        return Future<Int, Never> { promise in
            t = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { (t) in
                print("Timer!")
                promise(.success(i * i))
            }
        }
    }
    .sink { (i) in
        print("ok")
        print("\(Date()): \(i) received")
    }

计时器触发,但未调用接收器。将分配更改为 ...

let pub = Just(33)

...解决它。

【讨论】:

    猜你喜欢
    • 2019-10-26
    • 1970-01-01
    • 2017-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多