I have 2 steps in processing pipeline which runs over many images:
Without backpressure, all the work from Step 1 might pile up, leading to a high memory usage just for having images open.
I guess I could use a semaphore (e.g. of 5) which represents roughly the amount of memory I am willing to give for step 1 (5 pictures). I guess this would make 5 of my background threads to block, which is probably a bad thing? (that's a serious question: is it bad to block a background thread, since it consumes resources.)
If you're using Combine, flatMap
can provide the back pressure. FlatMap
creates a publisher for each value it receives, but exerts back pressure when it reaches the specified maximum number of publishers that haven't completed.
Here's a simplified example. Assuming you have the following functions:
func loadImage(url: URL) -> AnyPublisher<UIImage, Error> {
// ...
}
func doImageProcessing(image: UIImage) -> AnyPublisher<Void, Error> {
// ...
}
let urls: [URL] = [...] // many image URLs
let processing = urls.publisher
.flatMap(maxPublishers: .max(5)) { url in
loadImage(url: url)
.flatMap { uiImage in
doImageProcessing(image: uiImage)
}
}
In the example above, it will load 5 images, and start processing them. The 6th image will start loading when one of the earlier ones is done processing.