【问题标题】:Empty() publisher does not send completionEmpty() 发布者不发送完成
【发布时间】:2021-02-22 23:47:15
【问题描述】:

在这段代码中,我希望 Empty() 发布者向 .sink 订阅者发送完成,但没有发送完成。

func testEmpty () {
    let x = XCTestExpectation()

    let subject = PassthroughSubject<Int, Never>()

    emptyOrSubjectPublisher(subject).sink(receiveCompletion: { completion in
        dump(completion)
    }, receiveValue: { value in
        dump(value)
    }).store(in: &cancellables)

    subject.send(0)

    wait(for: [x], timeout: 10.0)
}

func emptyOrSubjectPublisher (_ subject: PassthroughSubject<Int, Never>) -> AnyPublisher<Int, Never> {
    subject
        .flatMap { (i: Int) -> AnyPublisher<Int, Never> in
            if i == 1 {
                return subject.eraseToAnyPublisher()
            } else {
                return Empty().eraseToAnyPublisher()
            }
        }
        .eraseToAnyPublisher()
}

为什么emptyOrSubjectPublisher 没有收到完成?

【问题讨论】:

    标签: swift combine


    【解决方案1】:

    Empty 完成,但整个管道没有完成,因为初始 Subject 尚未完成。产生 Empty 的内部管道(flatMap)已经“吞噬”了完成。这是预期的行为。

    您可以通过简单地在 flatMap 中生成 Just 来更轻松地看到这一点,例如Just(100):

        subject
        .flatMap {_ in Just(100) }
        .sink(receiveCompletion: { completion in
            print(completion)
        }, receiveValue: { value in
            print(value)
        }).store(in: &cancellables)
        subject.send(1)
    

    你知道,我知道 Just 发出一次并完成。但是尽管 Just 的 到达了管道,但没有完成。

    您可以很容易地看到它为什么会这样工作。如果我们有来自发布者的潜在值序列,但在 flatMap 中生成的某个中间发布者有能力完成整个管道并提前结束它,那将是非常错误的。

    (请参阅我的https://www.apeth.com/UnderstandingCombine/operators/operatorsTransformersBlockers/operatorsflatmap.html,我提出了同样的观点。)

    如果目标是向管道发送完成,则需要完成的是 subject。例如,你可以说

    func emptyOrSubjectPublisher (_ subject: PassthroughSubject<Int, Never>) -> AnyPublisher<Int, Never> {
        subject
            .flatMap { (i: Int) -> AnyPublisher<Int, Never> in
                if i == 1 {
                    return subject.eraseToAnyPublisher()
                } else {
                    subject.send(completion: .finished) // <--
                    return Empty().eraseToAnyPublisher()
                }
            }
            .eraseToAnyPublisher()
    }
    

    [但是请注意,您的整个emptyOrSubjectPublisher 是特殊的;目前尚不清楚它的目的是什么。当i1 时返回subject 也是毫无意义的,因为subject 在我们到达这里时已经发布了1,并且现在不会发布更多内容。因此,如果您在开始时发送1,您将不会收到1 作为值,因为您的flatMap 已经吞下了它并产生了一个不会发布的发布者。]

    【讨论】:

    • 好的,现在更有意义了 - 谢谢。也感谢阅读材料! emptyOrSubjectPublisher 是一个仓促编造的测试结构。
    • 没问题,很高兴看到大家对Combine 框架感兴趣。你的问题很好!
    • 记住flatMap 行为方式的更简单方法是,它将所有值事件和失败反映给父Publisher,但不反映完成的事件。
    • @SlashDevSlashGnoll 正如我所说,请参阅apeth.com/UnderstandingCombine/operators/…
    猜你喜欢
    • 1970-01-01
    • 2021-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多