合并:Just 和 first() 之间完成流的区别

Combine: difference between Just and first() to finish a stream

我是 Combine 的新手,我不了解这些情况下的行为:

func getPublisherWithFirst() -> AnyPublisher<Value, Error> {
    // somePublisher: PassthroughSubject<Bool, Never>
    return somePublisher
        .compactMap { (someBool) -> Value? in
            if someBool {
                return Value()
            }
            return nil
        }
        .setFailureType(to: Error.self)
        .first()
        .timeout(.seconds(5), scheduler: DispatchQueue.main)
        .eraseToAnyPublisher()
}
func getPublisherWithFlatMap() -> AnyPublisher<Value, Error> {
    // somePublisher: PassthroughSubject<Bool, Never>
    return somePublisher
        .flatMap { (someBool) -> AnyPublisher<Value, Never> in
            if someBool {
                return Just(Value()).eraseToAnyPublisher()
            }
            return Empty(completeImmediately: false, outputType: Value.self, failureType: Never.self).eraseToAnyPublisher()
        }
        .setFailureType(to: Error.self)
        .timeout(.seconds(5), scheduler: DispatchQueue.main)
        .eraseToAnyPublisher()
}

使用 getPublisherWithFlatMap 时,我的流没有完成(触发超时),即使返回 Just 发布者并且它应该完成。为什么?

“问题”是由于您使用 PassthroughSubject 作为您的上游造成的。 PassthroughSubject 永远不会完成,除非您明确发送完成。

当使用 first 时,上游是否完成并不重要,因为 first 在其上游发出单个值后立即完成。但是,在 flatMap 的情况下,flatMap 对其上游发出的每个值执行闭包,因此它仅在其上游完成时才完成。 return JustflatMap 中并不重要,Just 只会被 return 编辑为单个值,而不是上游作为一个整体。

如果你想确保 getPublisherWithFlatMap 在发出单个值后也完成,你也应该调用 first 它,类似于 getPublisherWithFirst.

func getPublisherWithFlatMap() -> AnyPublisher<Value, Error> {
    // somePublisher: PassthroughSubject<Bool, Never>
    return somePublisher
        .flatMap { (someBool) -> AnyPublisher<Value, Never> in
            if someBool {
                return Just(Value()).eraseToAnyPublisher()
            }
            return Empty(completeImmediately: false, outputType: Value.self, failureType: Never.self).eraseToAnyPublisher()
        }
        .setFailureType(to: Error.self)
        .first()
        .timeout(.seconds(5), scheduler: DispatchQueue.main)
        .eraseToAnyPublisher()
}