Here is a little test bed showing two Tasks that start at the same time, and whichever completes first cancels the other:
func startTest() async throws {
Task { await test() }
try await Task.sleep(nanoseconds: 1 * NSEC_PER_SEC)
// comment out this next line to test the timeout
NotificationCenter.default.post(name: .init("Test"), object: nil)
}
func test() async {
let notificationTask = Task {
for await _ in NotificationCenter.default
.notifications(named: .init("Test"), object: nil)
.prefix(1) {}
print("Done waiting for the notification")
}
let timeoutTask = Task {
try await Task.sleep(nanoseconds: 5 * NSEC_PER_SEC)
notificationTask.cancel()
print("I timed out and cancelled the notification task")
}
await notificationTask.value
timeoutTask.cancel()
print("finished!")
}
As you can see, the idea is that either we receive the notification within 5 seconds, in which case the five second timer is cancelled, or we time out in 5 seconds, in which case waiting for the notification is cancelled.
My question is: is there a neater way to express this? I tried various task group and async let formulations but didn't come up with anything. Maybe this is the "right" way but it grates somehow.
TaskGroupawait tg.next() to wait for the result of the "race winner"tg.cancelAll() to cancel the outstanding tasksimport Foundation
func startTest() async throws {
Task { try await test() }
try await Task.sleep(nanoseconds: 1 * NSEC_PER_SEC)
// comment out this next line to test the timeout
NotificationCenter.default.post(name: .init("Test"), object: nil)
}
func test() async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask { // formerlynotificationTask
for await _ in NotificationCenter.default
.notifications(named: .init("Test"), object: nil)
.prefix(1) {}
print("Done waiting for the notification")
}
group.addTask { // formerly "timeoutTask"
try await Task.sleep(nanoseconds: 5 * NSEC_PER_SEC)
print("I timed out and cancelled the notification task")
}
try await group.next() // Wait for the first completed task
group.cancelAll() // Cancel the rest
}
print("finished!")
}
try await startTest()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With