Networking, file I/O and most modern Apple APIs are async, so this is how real apps do anything slow.
An async function can suspend while waiting — for the network, disk or a timer — without blocking its thread. Mark it async and call it with await:
func fetchUser(id: Int) async throws -> User
let user = try await fetchUser(id: 1)Each await is a possible suspension point: other work may run before your function continues, and on resumption it may be on a different thread unless it is isolated to an actor. Code between awaits runs without interruption from other tasks on the same actor.
You can only call async functions from an async context. To start async work from synchronous code — a button handler, main — create a `Task`: Task { await refresh() }. A Task starts running immediately and inherits the current actor (for example, the main actor in UI code) and priority. let t = Task { ... } lets you await t.value later or t.cancel().
Combined with throws, the order is async throws in the declaration and try await at the call.
Foundation provides async APIs, e.g. let (data, response) = try await URLSession.shared.data(from: url), and try await Task.sleep(for: .seconds(1)).
Sequential awaits run one after another. To run things in parallel you need structured concurrency (async let, task groups), covered next. Async functions are also cancellable — see that lesson for how cancellation works.
Example
import Foundation
struct Weather { let city: String; let temp: Int }
func fetchWeather(for city: String) async throws -> Weather {
try await Task.sleep(for: .milliseconds(200)) // simulated network
guard !city.isEmpty else { throw URLError(.badURL) }
return Weather(city: city, temp: 21)
}
func report() async {
do {
let w = try await fetchWeather(for: "Oslo")
print("\(w.city): \(w.temp)°") // Oslo: 21°
_ = try await fetchWeather(for: "")
} catch {
print("failed:", error.localizedDescription)
}
}
let task = Task { await report() } // start from sync code
await task.value // top-level code may awaitRemember
asyncfunctions suspend at eachawaitwithout blocking a threadTask { }starts async work from synchronous code- Write
async throws, call withtry await
Watch out for
- Assuming state is unchanged after an
await— other code may have run meanwhile.