In the Combine framework, when using flatMap for irreparable processing requests using Web API, it is better to use the maxPublishers
argument and use .flatMap(maxPublishers: .max(1))
.
For example, by using .flatMap (maxPublishers: .max(1))
as shown below, the Web API POST will not process input events until the result is subscribed.
let cancelable = inputEvents .flatMap(maxPublishers: .max(1)) { // Example: irreparable processing requests createUser(name: $0.email, $0.password) } .sink { ... }
Verification code
Verify the operation with two source codes.
- Example without
maxPublishers
argument for flatMap-
.unlimited
by default
-
- Example using
.flatMap (maxPublishers: .max(1))
To conclude, .flatMap (maxPublishers: .max(1))
uses a flatMap to fix the number of Publishers created, even if there are new inputs until the result is subscribed. Do not execute.
Example without maxPublishers
argument in flatMap
- Spec
- Input events are
(1...3).publisher
- flatMap
- In the first event, asynchronously delay the number after 1 second and stream the number as a string
- After the first one, asynchronously stream numbers as strings
- Input events are
import Foundation import Combine import PlaygroundSupport let cancelable = (1...3).publisher .flatMap { value -> Future<String, Never> in print("🍏flatMap: \(value)") if value == 1 { return Future<String, Never> { promise in let v = value // delay the event of the stream created in the first shot. DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { print("🐢after: \(v)") promise(.success("\(v)")) } } } else { return Future<String, Never> { promise in let v = value DispatchQueue.main.async { print("🐰after : \(v)") promise(.success("\(v)")) } } } } .sink(receiveCompletion: { completion in switch completion { case .finished: print("🍎sink finished:", completion) case .failure(let error): print("🍎sink failure:", error) } }) { value in print("🍎sink received: \(String(describing: value))") } let page = PlaygroundPage.current page.needsIndefiniteExecution = true DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) { // Instructs Xcode that the playground page has finished execution. print("🕘finished") page.finishExecution() }
This code has the following output:
🍏flatMap: 1 🍏flatMap: 2 🍏flatMap: 3 🐰after : 2 🍎sink received: 2 🐰after : 3 🍎sink received: 3 🐢after: 1 🍎sink received: 1 🍎sink finished: finished 🕘finished
Use asncAfter(deadline: .now() + 1.0)
to delay the event of the first stream created. However, events flow one after another and switch to a new stream with flatMap.
Example of using .flatMap (maxPublishers: .max(1))
Change the following from the previous code
.flatMap { value -> Future<String, Error> in
↓
.flatMap(maxPublishers: .max(1)) { value -> Future<String, Error> in
This code has the following output:
🍏flatMap: 1 🐢after: 1 🍎sink received: 1 🍏flatMap: 2 🐰after : 2 🍎sink received: 2 🍏flatMap: 3 🐰after : 3 🍎sink received: 3 🍎sink finished: finished 🕘finished
As before, use asncAfter(deadline: .now() + 1.0)
to delay the event of the first stream created. It seems that the next event occurs after the delayed event is sink received: 1
.
Conclusion
- If you do not use the
maxPublishers
argument in flatMap, event creation does not wait for the end.- It does not use Back-Pressure.
- If you specify
max(1)
formaxPublishers
, event creation waits for completion.- It is requesting 1 as Back-Pressure.
- The request repeats with each subscription.
- Even if you specify
max(1)
, the subscription does not end when you create a one-time event.
- It is requesting 1 as Back-Pressure.
Top comments (0)