Maybe I should propose yet another model... one that does not care about scopes:
-
Each
async letfound in the function, whether in a loop or elsewhere, can have zero or one task running at a time. They're all implicitly awaited at the end of the function. -
When the
async letis inside of a loop, and not awaited on all paths, you need to declare it asawait async let. That's because the second loop iteration will need toawaitthe task from the first iteration before it can launch its new task.
I think this is an interesting model because you don't need to change anything but the async let declaration itself, yet all the suspension points are clearly marked.
Example:
func test() async {
async let x = work(0)
for i in 1..<10 {
async let y = work(i)
await async z = work(i)
if await y == 0 {
await print(z)
}
}
// end of function: implicitly awaiting x, y, z
}
Here, z is not awaited on all paths, so it needs to become await async let to allow itself to await the termination of the previous iteration's z.