Inversely related to @toph42 and @michelf's concerns about implicitly awaiting cancellation at scope exit is the current inability to neatly await many async lets without caring about their results. This is especially true for async -> Void functions, but is generally true for any async function which has side effects you want without the value.
Say you have some async action functions returning Void. Sometimes you want to perform them individually, sometimes all at once, in parallel. In all cases you want them to complete. Currently, it's hard to express the await all functionality. async let _ is disallowed. Simply not awaiting the functions results in cancellation. Currently, we must explicitly name and await every value.
func performAll() async {
async let x = x()
async let y = y()
async let z = z()
_ = await (x, y, z)
}
Or perhaps we could allow async let _ when using @michelf's dangling await?
func performAll() async {
async let _ = x()
async let _ = y()
async let _ = z()
await
}
Or perhaps allow async let itself as a scope?
func performAll() async {
await async let {
x()
y()
z()
}
}
Perhaps we could allow async let _ in a particular context?
func performAll() async {
await {
async let _ = x()
async let _ = y()
async let _ = z()
}
}
Even better, perhaps we can drop the async let when we don't care about the values, and just add the await scope directly?
func performAll() async {
await {
x()
y()
z()
}
}
I like the look of an await scope. I wonder if it's generally composable. What rules do we want in it? Perhaps its only new rule is the implicit async let nature of unawaited async results. Or perhaps that's too much.
Of course, we go the TaskGroup route and await separate references.
func performAll() async {
await all(x, y, z)
}
func all<T>(_ actions: (() async -> T)...) async {
await withTaskGroup(of: T.self) { group in
for action in actions {
group.async { await action() }
}
}
}
Except that won't work with parameters, barring more wrapping, at which point we've lost most of the convenience.
Perhaps this isn't that big of an issue. It just seems awkward to have a brand new feature that doesn't handle such a case very elegantly.