Using async let with synchronous functions

Hi folks,

I’m looking for clarification around async let usage with synchronous functions. The snippet below compiles, and at runtime it does seem to run f and g concurrently (they are executing on different off-main threads).

My confusion is that f and g are not annotated as async, which I thought was a requirement for using async let.

I did a quick grep through the swift book and didn’t see anything documenting this behavior. Is this a blessed usage of async let?

I’m using the following settings:

  • Approachable concurrency: off
  • Default actor isolation: nonisolated
  • Strict concurrency checking: complete
  • Swift language version: 6

@MainActor
struct ContentView: View {
    var body: some View {
        EmptyView()
            .task {
                print(await expensive())
            }
    }
    nonisolated func expensive() async -> Int {
        whichThread()
        async let f = f()
        async let g = g()
        return await f + g
    }
}

func whichThread() {
    print("Expensive is on thread: \(Thread.current)")
}

nonisolated func f() -> Int {
    print("f is on thread: \(Thread.current)")
    return 1
}

nonisolated func g() -> Int {
    print("g is on thread: \(Thread.current)")
    return 2
}

Thank you,
Lou

In SE-0317, the only warning about synchronous usage is that “It is not allowed to declare async let as top-level code, in synchronous functions or closures.”

So, while you can only use async let from within an asynchronous context, it says nothing about prohibiting async let to call a function that is synchronous. In fact, SE-0317 has some examples (on different topics), where they do precisely that.

3 Likes