somu
(somu)
1
Overview
- I have an async function (swift concurrent function) that I would like to execute every time a notification is fired.
- I would like the async function to run sequentially (the function should complete before next one begins)
My attempt:
- This is not the actual project, I have tried to simulate a simplified example posted below
- It could be possible that in trying to simulate the problem I have created an incorrect code to simulate the problem.
Questions:
- Does this guarantee sequential execution?
- Or is there a better approach? (using combine or DispatchQueue or some other way?)
Code
extension Notification.Name {
static let dummy = Notification.Name(rawValue: "dummy")
}
let notifications = NotificationCenter.default.notifications(named: .dummy)
let merger = Merger()
var counter = 0
class Merger {
func processChanges(forCounter counter: Int) async throws {
print("process \(counter) started")
try await Task.sleep(nanoseconds: 6_000_000_000)
print("process \(counter) ended")
}
}
let anyCancellable = Timer.TimerPublisher(interval: 1, runLoop: .main, mode: .default)
.autoconnect()
.sink { _ in
// print("timer fired")
NotificationCenter.default.post(name: .dummy, object: nil)
}
try? await merger.processChanges(forCounter: counter + 100)
for await notification in notifications {
counter += 1
try await merger.processChanges(forCounter: counter)
}
lukasa
(Cory Benfield)
2
As written, yes. The call to try await merger.processChanges(forCounter: counter) will suspend that loop for as long as it takes processChanges to return. Until processChanges has run to completion (including anything that it itself awaits on, such as Task.sleep), the next iteration will not happen.
1 Like
somu
(somu)
3
@lukasa Thanks a lot for clear explanation as to why it works.
In the real world, the next challenge for me is to ensure everything passed to processChanges is sendable and there is that clear separation and nothing crosses concurrency boundaries (which I often encounter)