async/await Fundamentals

33.1 Async Functions

await marks suspension points. The function may pause and resume (possibly on a different thread). Code reads sequentially, without callback pyramids.

func fetchUser(id: Int) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!
    let (data, response) = try await URLSession.shared.data(from: url)
    guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw URLError(.badServerResponse) }
    return try JSONDecoder().decode(User.self, from: data)
}

func loadProfile() async {
    do {
        let user = try await fetchUser(id: 1)      // suspension point: the thread is freed while waiting
        print(user.name)
    } catch {
        print("Failed: \(error)")
    }
}

33.2 Calling Async Code

// From synchronous code: create a Task
Task {
    await loadProfile()
}

// Async entry point
@main
struct App {
    static func main() async throws {
        let user = try await fetchUser(id: 1)
        print(user)
    }
}

// SwiftUI: .task modifier (cancelled automatically when the view disappears)
// Text(name).task { name = await loadName() }

33.3 Async Properties, Sequences and Concurrent Bindings

struct Photo {
    var thumbnail: Data {
        get async throws { try await download("thumb") }   // async computed property
    }
}

async let first = fetchUser(id: 1)            // start concurrently
async let second = fetchUser(id: 2)
let users = try await [first, second]         // await both

for try await line in URL(fileURLWithPath: "log.txt").lines {   // AsyncSequence
    print(line)
}