// *** before: ***
let group = DispatchGroup()
var first: Data?
var second: Data?
group.enter()
loadOld("a") { first = $0; group.leave() }
group.enter()
loadOld("b") { second = $0; group.leave() }
group.notify(queue: .main) { use(first, second) }
// two shared variables, two leaves that must not be missed, and no way to
// cancel the pair once it started
// *** in version 5.5: ***
func loadBoth() async throws -> (Data, Data) {
async let first = load("a") // both start right here
async let second = load("b")
return try await (first, second) // and here we wait for both
}
// a task is the bridge from ordinary code into the asynchronous world
let task = Task {
let pair = try await loadBoth()
print(pair)
}
task.cancel() // the cancellation reaches the children of the task as well
// a child task cannot outlive its parent: that is what "structured" means