Swift 如何防止PassthroughSubject在并发上游期货完成之前杀死.sink?
我有一个PassthroughSubject,它发送30个整数,后跟一个finish消息 当我从主体那里收到这些数字时,我产生了一个睡眠一秒钟的未来,并以输入数字*2完成 我使用.receiveOn确保futures并发运行,但这意味着finish消息也会通过链并发传播 在所有期货交易结束前结束 任何RxSwift/联合收割机向导都知道我如何才能使完成消息的接收被延迟 以下是实现所述行为的游乐场:Swift 如何防止PassthroughSubject在并发上游期货完成之前杀死.sink?,swift,ios13,combine,Swift,Ios13,Combine,我有一个PassthroughSubject,它发送30个整数,后跟一个finish消息 当我从主体那里收到这些数字时,我产生了一个睡眠一秒钟的未来,并以输入数字*2完成 我使用.receiveOn确保futures并发运行,但这意味着finish消息也会通过链并发传播 在所有期货交易结束前结束 任何RxSwift/联合收割机向导都知道我如何才能使完成消息的接收被延迟 以下是实现所述行为的游乐场: import Foundation import Combine import Playgroun
import Foundation
import Combine
import PlaygroundSupport
/// Setting up the playground
PlaygroundPage.current.needsIndefiniteExecution = true
/// Injects numbers 0-30 into combine message stream, and then sends a finish.
func publishNumbers(to subject: PassthroughSubject<Int, Error>) {
(0..<30).forEach {
subject.send($0)
}
subject.send(completion: .finished)
}
/// Delays for one secont, and completes the future by doubling the input.
func delayAndDoubleNumber(_ int: Int) -> Future<Int, Error> {
return Future<Int, Error> { complete in
sleep(1)
complete(.success(int * 2))
}
}
// Properties involved in Combine processing chain.
let numbersSubject = PassthroughSubject<Int, Error>()
let processingQueue = DispatchQueue.global(qos: .userInitiated)
// Combine processing chain
numbersSubject
.receive(on: processingQueue) //Comment this line to observe that all futures finish, and are collected before the finish message kills the sink.
.flatMap { number in
return delayAndDoubleNumber(number)
}
.collect(4)
.sink(receiveCompletion: { completion in
print("Complete: \(completion)")
}, receiveValue: { value in
print("Received Value: \(value)")
})
publishNumbers(to: numbersSubject)
<代码>导入基础
进口联合收割机
导入PlaygroundSupport
///设置操场
PlaygroundPage.current.NeedsDefiniteExecution=true
///将数字0-30注入组合消息流,然后发送一个finish。
func PublishNumber(主题:传递主题){
(0)未来{
返回未来{complete in
睡眠(1)
完成(.success(int*2))
}
}
//联合收割机处理链中涉及的属性。
let numbersubject=PassthroughSubject()
让processingQueue=DispatchQueue.global(qos:.userInitiated)
//联合加工链
数字主题
.receive(on:processingQueue)//注释此行,以观察在finish消息终止接收器之前,是否已收集所有未来和。
.flatMap{中的数字
返回delayAndDoubleNumber(数字)
}
.收集(4)
.sink(receiveCompletion:{completion in
打印(“完成:\(完成)”)
},receiveValue:{中的值
打印(“接收值:\(值)”)
})
PublishNumber(收件人:numbersSubject)
免责声明,这可能是对文档的错误解释,但我认为您应该使用
订阅(on:)
操作符,而不是接收(on:)
:
与影响下游消息的receive(on:options:)不同,subscribe(on:)更改上游消息的执行上下文
我对此的解释是,如果希望队列中发出来自numbersubject
的事件,可以使用subscribe(on:)
,例如:
numbersSubject
.flatMap { number in
return delayAndDoubleNumber(number)
}
.collect(4)
.subscribe(on: processingQueue)
.receive(on: RunLoop.main)
.sink(receiveCompletion: { completion in
print("Complete: \(completion)")
}, receiveValue: { value in
print("Received Value: \(value)")
})
从Xcode 11 beta 3开始,您不能将并发队列与Combine一起使用。您应该能够在Xcode 11 GM之前使用 菲利普·豪斯勒(Philippe Hausler)是一位从事联合收割机工作的苹果工程师。他在上说: 另外值得注意的是,用作调度器的
调度队列
必须始终是串行的,以遵守联合收割机操作员的合同
他说:
因此,接下来,在传播下游事件的方式方面有一些变化。现在,我们能够满足1.03的约束,即使DispatchQueue是并发的,或者OperationQueue不是maxConcurrentOperations的限制1,或者任何有效的调度程序是并发的,我们将始终在请求的调度程序上为.receive(on:)
发送序列化事件。我们稍微偏离规范的另一个警告是,在我们的世界中,诸如cancel()
和request(:)
等上游事件可以同时发生。也就是说,我们确实以线程安全的方式处理它们
您可以在Xcode 11 beta 3中通过调度到并发队列,然后从您的未来
的闭包中返回到主队列来实现并发:
import Foundation
import Combine
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
func delayAndDoubleNumber(_ int: Int) -> Future<Int, Never> {
return Future<Int, Never> { complete in
DispatchQueue.global(qos: .userInitiated).async {
sleep(1)
DispatchQueue.main.async {
complete(.success(int * 2))
}
}
}
}
let subject = PassthroughSubject<Int, Never>()
subject
.flatMap { delayAndDoubleNumber($0) }
.collect(4)
.sink(
receiveCompletion: { print("Complete: \($0)") },
receiveValue: { print("Received Value: \($0)") })
let canceller = (0 ..< 30).publisher().subscribe(subject)
<代码>导入基础
进口联合收割机
导入PlaygroundSupport
PlaygroundPage.current.NeedsDefiniteExecution=true
func delayAndDoubleNumber(int:int)->Future{
返回未来{complete in
DispatchQueue.global(qos:.userInitiated).async{
睡眠(1)
DispatchQueue.main.async{
完成(.success(int*2))
}
}
}
}
let subject=PassthroughSubject()
主题
.flatMap{delayAndDoubleNumber($0)}
.收集(4)
.水槽(
receiveCompletion:{打印(“完成:\($0)”)},
接收值:{print(“接收值:\($0)”)})
让取消器=(0..<30).publisher().subscribe(主题)
与Rob提出的解决方案不同,在接收器上调用cancel似乎不会取消任何内容。接收器仍然会接收所有值,并处理完成消息。哇,感谢链接到swift.org board。我不知道Beta在此阶段的串行队列要求。此解决方案有效s、 但它并不是唯一的combine-y。我想留下一个问题,以防combine的功能在今年秋天发布之前发生变化。