Swift 如何在Combine中限制flatMap并发性,但仍处理所有源事件?

Swift 如何在Combine中限制flatMap并发性,但仍处理所有源事件?,swift,combine,Swift,Combine,如果我指定maxpublisher参数,则第一个maxpublisher事件之后的源事件将不会被平面映射。而我只想限制并发性。也就是说,在第一批MaxPublisher平面地图发布程序完成后,将继续处理下一个事件 Publishers.Merge( addImageRequestSubject .flatMap(maxPublishers: .max(3)) { self.compressImage($0) } .compactMap { $0 }

如果我指定
maxpublisher
参数,则第一个maxpublisher事件之后的源事件将不会被平面映射。而我只想限制并发性。也就是说,在第一批MaxPublisher平面地图发布程序完成后,将继续处理下一个事件

Publishers.Merge(
    addImageRequestSubject
        .flatMap(maxPublishers: .max(3)) { self.compressImage($0) }
        .compactMap { $0 }
        .flatMap(maxPublishers: .max(3)) { self.addImage($0) },
    addVideoRequestSubject
        .flatMap(maxPublishers: .max(3)) { self.addVideo(url: $0) }
).sink(receiveCompletion: { _ in }, receiveValue: {})
.store(in: &cancelBag)
我还尝试通过OperationQueue来限制并发性。但是
maxConcurrentOperationCount
似乎没有效果

Publishers.Merge(
    addImageRequestSubject
        .receive(on: imageCompressionQueue)
        .flatMap { self.compressImage($0) }
        .compactMap { $0 }
        .receive(on: mediaAddingQueue)
        .flatMap { self.addImage($0) },
    addVideoRequestSubject
        .receive(on: mediaAddingQueue)
        .flatMap { self.addVideo(url: $0) }
).sink(receiveCompletion: { _ in }, receiveValue: {})
.store(in: &cancelBag)

private lazy var imageCompressionQueue: OperationQueue = {
    var queue = OperationQueue()
    queue.maxConcurrentOperationCount = 3

    return queue
}()

private lazy var mediaAddingQueue: OperationQueue = {
    var queue = OperationQueue()
    queue.maxConcurrentOperationCount = 3

    return queue
}()
平面地图发布者是这样看的:

func compressImage(_ image: UIImage) -> Future<Data?, Never> {
    Future { promise in
        DispatchQueue.global().async {
            let result = image.compressTo(15)?.jpegData(compressionQuality: 1)
            promise(Result.success(result))
        }
    }
}
func compressImage(image:UIImage)->未来{
未来{
DispatchQueue.global().async{
让结果=image.compressTo(15)?.jpeg数据(压缩质量:1)
承诺(结果、成功(结果))
}
}
}

您已经非常顺利地进入了
.buffer
操作符的用例。其目的是通过累积原本会下降的值来补偿
.flatMap
背压

我将用一个完全人为的例子来说明:

class ViewController: UIViewController {
    let sub = PassthroughSubject<Int,Never>()
    var storage = Set<AnyCancellable>()
    var timer : Timer!
    override func viewDidLoad() {
        super.viewDidLoad()
        sub
            .flatMap(maxPublishers:.max(3)) { i in
                return Just(i)
                    .delay(for: 3, scheduler: DispatchQueue.main)
                    .eraseToAnyPublisher()
            }
            .sink { print($0) }
            .store(in: &storage)
        
        var count = 0
        self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { 
            _ in
            count += 1
            self.sub.send(count)
        }
    }
}
解决方案是在
flatMap
前面放置一个缓冲区。它需要足够大,以容纳任何丢失的值足够长的时间,以便请求它们:

        sub
            .buffer(size: 20, prefetch: .keepFull, whenFull: .dropOldest)
            .flatMap(maxPublishers:.max(3)) { i in

结果是所有的数值实际上都到达了
接收器
。当然,在现实生活中,如果缓冲区不够大,无法补偿出版商的价值释放率与反压
平面图的价值释放率之间的差异,我们仍然可能会失去价值。谢谢您的回复。我试图推迟我的未来,但仍然只有3张图片正在处理中。你的意思是我以不适当的方式切换线程,应该使用receiveOn?@matt关于MaxPublisher,当flatMap操作符加载指定的最大发布服务器数时,会忽略发送的那些源事件。在flatMap操作完成后,我可以发送新的源事件,它们将被成功处理。这就是我所能观察到的。但我需要处理所有源事件,以满足对flatMap操作符的需求。好吧,也许你需要添加一个缓冲区?@matt你说得对,缓冲区是我错过的!是否要添加答案?
        sub
            .buffer(size: 20, prefetch: .keepFull, whenFull: .dropOldest)
            .flatMap(maxPublishers:.max(3)) { i in