iOS中Combine Framework如何为Publisher发射的值添加间隔延迟?
Combine延迟接收事件问题解决方案
问题分析
你的代码存在几个关键问题导致delay操作符未生效:
- 操作符链断裂:
delay之后没有通过链式调用连接sink,而是直接订阅原subject,等于完全绕过了延迟处理逻辑。 - 未保留订阅者:
sink返回的AnyCancellable如果不存储,会被立即释放,订阅直接失效。 - Playground执行限制:默认情况下Playground会在代码执行完后立即终止,无法等待延迟后的事件输出。
delay的作用逻辑:delay是给每个事件单独延迟指定时长,如果两个事件同时发送,延迟后依然会同时被接收,无法实现两个值之间的间隔延迟。
修正方案
要实现两个值之间的延迟接收,我们可以通过两种方式处理,以下是完整的修正代码:
方案一:用Combine操作符实现顺序延迟
import Combine import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true example(of: "delay between values"){ struct Human { let fName: String let lName: String } let subject = PassthroughSubject<Human,Error>() let delayInSeconds = 4 var isLoading = false var cancellables = Set<AnyCancellable>() subject .flatMap { human in // 每个值延迟指定时长后发送,实现顺序间隔 Just(human) .delay(for: .seconds(delayInSeconds), scheduler: DispatchQueue.main) } .sink( receiveCompletion:{ print("Received Completion", $0) isLoading = true print("isLoading =", isLoading) PlaygroundPage.current.finishExecution() }, receiveValue: { print("Value Received:" ,$0.fName, $0.lName) }) .store(in: &cancellables) subject.send(Human(fName: "John", lName: "Abraham")) subject.send(Human(fName: "Jane", lName: "Anne")) subject.send(completion: .finished) }
方案二:手动延迟发送第二个值(更直接)
import Combine import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true example(of: "delay between values"){ struct Human { let fName: String let lName: String } let subject = PassthroughSubject<Human,Error>() let delayInSeconds = 4 var isLoading = false var cancellables = Set<AnyCancellable>() subject .sink( receiveCompletion:{ print("Received Completion", $0) isLoading = true print("isLoading =", isLoading) PlaygroundPage.current.finishExecution() }, receiveValue: { print("Value Received:" ,$0.fName, $0.lName) }) .store(in: &cancellables) // 第一个值立即发送 subject.send(Human(fName: "John", lName: "Abraham")) // 延迟指定时长后发送第二个值 DispatchQueue.main.asyncAfter(deadline: .now() + delayInSeconds) { subject.send(Human(fName: "Jane", lName: "Anne")) subject.send(completion: .finished) } }
关键注意点
- 必须将
sink返回的AnyCancellable存储到集合中,否则订阅会被系统立即释放,无法接收事件。 - Playground必须开启
needsIndefiniteExecution = true,否则会在初始代码执行完后直接终止,看不到延迟后的输出。
内容的提问来源于stack exchange,提问作者D.Yasiru Jayatissa
相关产品推荐
相关产品推荐

