How are async and await mechanisms implemented under the hood?
sobes.tech AI
Answer from AI
Async/await in Swift is built on structured concurrency and uses Dispatch/Global Actors. Under the hood, the following mechanisms operate:
-
Continuation Style: Asynchronous functions are compiled into a continuation style. Instead of returning a value immediately, control is transferred to the Swift runtime, which saves the current state of the function (local variables, execution pointer). When the asynchronous operation completes, the runtime resumes the function from the saved state.
-
Task: Each call to an
asyncfunction creates or joins a structuredTask. ATaskrepresents a unit of work and forms a hierarchy. The parentTaskwaits for the completion of its childTasks. -
Job: A
Taskis broken down into smaller units of work calledJobs. AJobrepresents a fragment of code that can be executed on an executor. -
Executor: The executor is responsible for running
Jobs. The standard library uses globalDispatchQueues as executors for most asynchronous tasks. Specific contexts (e.g.,MainActor) have their own specialized executors. -
Suspension Points:
awaitis a suspension point. When reachingawait, the function suspends, control is transferred to the caller or executor, and the currentJobis completed. The Swift runtime saves the function's state. -
Resumption: When the awaited asynchronous operation completes, the Swift runtime creates a new
Jobto continue executing the suspended function. ThisJobis queued on an executor. -
Cancellation: Structured concurrency supports hierarchical cancellation. Cancelling a parent
Taskautomatically cancels all its childTasks. Asynchronous operations can check the cancellation status and react accordingly.
Here is a simplified example of compiling an async function:
// Original async function
func fetchData() async -> Data {
// Asynchronous operation
let data = await URLSession.shared.data(from: url).0
return data
}
// Simplified compilation representation
// Instead of returning, the function takes a Continuation
func fetchData(_ continuation: Continuation<Data, Error>) {
// Initial code of the function
let url = URL(string: "...")! // Example
// Start asynchronous operation
URLSession.shared.data(from: url) { data, _, error in
// This closure executes after the async operation completes
if let data = data {
// Pass the result to the continuation, resuming the function
continuation.resume(returning: data)
} else if let error = error {
continuation.resume(throwing: error)
} else {
// Handle other cases
continuation.resume(throwing: NSError(domain: "", code: 0, userInfo: nil))
}
}
// The function ends here, waiting for resume to be called by the continuation
}
Await is essentially syntactic sugar over Continuation management and Job scheduling on the executor. Suspension does not block the thread directly but allows the executor to run other Jobs.