01 · INTRODUCTION
What Does await Mean in Swift?
The short answer
The await keyword marks a point where the current Swift task may suspend while an asynchronous operation produces its result.
If suspension is necessary, the task stops executing at that point. Its current thread becomes available for other eligible work, and the task continues later when the awaited operation is ready.
EXECUTION DIAGRAM
let launches = try await loadRocketLaunches()
// ▲
// │
// the current task may suspend here
await does not create a task, start a background thread or guarantee that suspension will occur.
💡 Important Terminology
Potential suspension point = a marked place where a task is permitted to suspend.
Suspended task = a task whose next instructions are not currently eligible to execute.
Continuation = the remaining work that can continue after the awaited operation becomes ready.
02 · TUTORIAL
async Declares, await Calls
In What Does async Mean in Swift?, we marked the function as one that may suspend:
CODE EXAMPLE
func loadRocketLaunches() async throws -> [Launch] {
// Asynchronous implementation
}
The caller acknowledges that contract using await:
CODE EXAMPLE
let launches = try await loadRocketLaunches()
EXECUTION DIAGRAM
FUNCTION DECLARATION
func loadRocketLaunches() async throws -> [Launch]
▲
│
may suspend
FUNCTION CALL
let launches = try await loadRocketLaunches()
▲
│
suspension may occur here
async belongs to the function declaration and type.
await belongs to the expression that calls asynchronous code.
03 · TUTORIAL
Begin With an Ordinary Function Call
A synchronous call returns before the caller continues:
CODE EXAMPLE
let launches = try loadSavedLaunches()
show(launches)
EXECUTION DIAGRAM
SYNCHRONOUS CALL
Enter loadSavedLaunches()
│
▼
Execute the complete function
│
▼
Return launches
│
▼
Execute show(launches)
The calling thread remains occupied while the synchronous function executes.
If the function performs a blocking wait, that thread cannot use the same time to execute other work.
04 · TUTORIAL
An Awaited Call Preserves the Same Reading Order
Asynchronous Swift deliberately retains the familiar top-to-bottom shape:
CODE EXAMPLE
let launches = try await loadRocketLaunches()
show(launches)
show(launches) does not execute before loadRocketLaunches() produces its result.
The task may suspend, but the logical order remains:
EXECUTION DIAGRAM
AWAITED CALL
Begin loadRocketLaunches()
│
▼
Possible suspension
│
▼
Receive [Launch]
│
▼
Assign launches
│
▼
Execute show(launches)
This is one of the most useful properties of await.
The asynchronous operation can stop occupying its thread while it waits without forcing the continuation of our function into a separate completion-handler closure.
05 · TUTORIAL
What Happens When the Task Reaches await?
The current task calls the asynchronous function.
Two broad outcomes are possible.
EXECUTION DIAGRAM
REACH await
│
├── Operation can produce its result now
│ │
│ ▼
│ Task continues executing
│
└── Operation must wait
│
▼
Task suspends
│
▼
Thread can execute other work
│
▼
Result becomes ready
│
▼
Task becomes eligible to continue
The call may pass through several asynchronous functions before the deepest operation actually needs to wait.
When that operation suspends, the asynchronous callers waiting for its result are suspended as part of the same task.
06 · TUTORIAL
await Does Not Always Suspend
await marks a potential suspension point rather than a guaranteed pause.
CODE EXAMPLE
actor LaunchCache {
private var launches: [Launch] = []
func savedLaunches() -> [Launch] {
launches
}
}
let launches = await cache.savedLaunches()
The actor crossing requires await because the caller may need to wait for access to the actor.
If the actor is available when the call is scheduled, the wait may be extremely short. The language does not promise that every execution of the expression will visibly suspend.
The programmer should read await as:
💡 Remember
Execution may suspend here.
Do not build program correctness around an assumption that it definitely will—or definitely will not.
07 · TUTORIAL
Suspending the Task Does Not Block the Thread
Consider a network request that takes two seconds:
CODE EXAMPLE
func loadRocketLaunches() async throws -> [Launch] {
let (data, _) = try await URLSession.shared.data(
from: launchesURL
)
return try JSONDecoder().decode([Launch].self, from: data)
}
When the request must wait for the server, Swift can suspend the task.
EXECUTION DIAGRAM
TASK
████ start request ████
│
▼
suspended
│
│ network request continues
│ outside the task's Swift execution
▼
████ decode response ████
THREAD USED BEFORE SUSPENSION
████ Task A ████ Task B ████ Task C ████
The task still exists throughout the wait.
It does not require its previous thread merely to remember where the function should continue.
This is suspension rather than blocking.
08 · TUTORIAL
await Does Not Mean Background Thread
The keyword does not instruct Swift to move the function onto a background thread.
CODE EXAMPLE
@MainActor
func refreshLaunches() async throws {
status = "Loading"
launches = try await api.loadLaunches()
status = "Finished"
}
The function is isolated to the main actor.
Before the asynchronous call, its isolated state changes execute through the main actor. If the task suspends, other main-actor work can make progress. When this function continues and accesses its isolated state again, it does so through the main actor.
await describes a possible interruption in the task's execution. Actor isolation describes where isolated work belongs. They are different contracts.
09 · TUTORIAL
await Does Not Create Concurrency
Two awaited calls written one after the other are normally sequential:
CODE EXAMPLE
let launches = try await loadRocketLaunches()
let agencies = try await loadSpaceAgencies()
The second call begins only after the first call has produced its result.
EXECUTION DIAGRAM
SEQUENTIAL AWAIT
loadRocketLaunches()
████████████████████
loadSpaceAgencies()
████████████████████
Suspension can allow unrelated tasks to make progress during either wait.
It does not make these two operations concurrent with each other.
Expressing independent child operations is the job of structured concurrency, which we will introduce after the execution model is complete.
10 · TUTORIAL
Code Between await Expressions Is Synchronous
A task does not spontaneously suspend between arbitrary Swift instructions.
CODE EXAMPLE
@MainActor
func refreshLaunches() async throws {
status = "Loading"
requestCount += 1
let result = try await api.loadLaunches()
launches = result
status = "Finished"
}
The first two state changes execute synchronously within the task before it reaches await.
After the awaited call produces its result and the task continues through the main actor, the final two state changes execute synchronously until the function reaches another potential suspension point or returns.
EXECUTION DIAGRAM
MAIN-ACTOR TASK
SYNCHRONOUS PERIOD 1
status = "Loading"
requestCount += 1
│
▼
await
│
possible
suspension
│
▼
SYNCHRONOUS PERIOD 2
launches = result
status = "Finished"
A long calculation inside either synchronous period can still occupy the executor and delay other work.
await creates visible potential suspension boundaries. It does not make every line cooperatively interruptible.
11 · TUTORIAL
State May Change While a Task Is Suspended
The current task can stop executing at await.
That means other eligible work may run before the task continues.
CODE EXAMPLE
@MainActor
func refreshLaunches() async throws {
let requestedAgency = selectedAgency
let result = try await api.loadLaunches(
for: requestedAgency
)
// selectedAgency may have changed while this task waited.
launches = result
}
EXECUTION DIAGRAM
Task A reads selectedAgency = "NASA"
│
▼
await
│
├── Task A is suspended
│
├── user selects "ESA"
│
└── other main-actor work updates state
│
▼
Task A continues with the NASA result
The task's local constant requestedAgency remains "NASA", but the feature's mutable state may no longer describe the same request.
This does not make await unsafe. It makes the suspension boundary important.
The complete rules for actor reentrancy belong to a later article. For now, remember that values read before an await may need to be reconsidered after it.
💡 Important
An await can divide one operation into two separately scheduled periods.
Shared mutable state may change while the task is suspended between them.
12 · TUTORIAL
await Preserves Errors and Return Values
An awaited function can return a value in the familiar way:
CODE EXAMPLE
let launches = await cachedLaunches()
If the asynchronous function can throw, the call uses both try and await:
CODE EXAMPLE
let launches = try await loadRocketLaunches()
try acknowledges that the call may throw.
await acknowledges that the call may suspend.
CODE EXAMPLE
do {
let launches = try await loadRocketLaunches()
show(launches)
} catch {
show(error)
}
When the task continues, the expression either produces its return value or throws its error through the ordinary Swift control flow.
13 · TUTORIAL
A Complete Feature Using await
The launch API declares its asynchronous contract:
CODE EXAMPLE
struct LaunchAPI {
func loadLaunches() async throws -> [Launch] {
let (data, _) = try await URLSession.shared.data(
from: launchesURL
)
return try JSONDecoder().decode(
[Launch].self,
from: data
)
}
}
The feature awaits that result:
CODE EXAMPLE
@MainActor
final class LaunchListFeature: ObservableObject {
@Published private(set) var launches: [Launch] = []
@Published private(set) var isLoading = false
@Published private(set) var errorMessage: String?
private let api: LaunchAPI
init(api: LaunchAPI) {
self.api = api
}
func refresh() async {
isLoading = true
errorMessage = nil
defer { isLoading = false }
do {
let result = try await api.loadLaunches()
launches = result
} catch {
errorMessage = error.localizedDescription
}
}
}
Before await, the feature enters its loading state.
If the network operation suspends, the main actor can execute other eligible work rather than remaining blocked by this task's wait.
After the result becomes available, the task continues through the main actor and updates the visible state.
The function still reads from top to bottom. await exposes the one place where its execution may be divided.
14 · TUTORIAL
The Complete Mental Model
EXECUTION DIAGRAM
CURRENT TASK
│
▼
Execute synchronous instructions
│
▼
Reach await
│
├── Result ready ──────────────┐
│ │
└── Must wait │
│ │
▼ │
Task suspends │
│ │
Thread is free │
│ │
Result becomes ready │
│ │
Task becomes runnable │
│ │
└──────────────────────┘
│
▼
Continue after await
│
▼
Return, throw or await again
await marks a potential suspension point in the current task.
The task may stop executing while its asynchronous dependency is incomplete. Its thread can then perform other eligible work.
When the dependency is ready, the task becomes eligible to continue through the appropriate executor.
The code after await remains ordered after the awaited result.
That is what await means in Swift.
15 · TUTORIAL
What to Remember
💡 What to Remember
1. await marks a potential suspension point in the current task.
2. The task may suspend when the asynchronous operation cannot produce its result immediately.
3. Suspension does not require the task to reserve its current thread.
4. await does not guarantee suspension.
5. await does not create a task or choose a background thread.
6. Code after await runs only after the awaited expression produces a value or throws.
7. Two consecutive awaited calls are sequential unless additional child tasks are created.
8. Code between potential suspension points executes synchronously within the current task.
9. Other eligible work may change shared state while the current task is suspended.
10. try await acknowledges two independent effects: throwing and possible suspension.
16 · TUTORIAL
Frequently Asked Questions
What does await do in Swift?
await marks a call where the current task may suspend until an asynchronous operation can produce its result. If suspension occurs, the task can continue later without reserving its previous thread.
Does await always suspend?
No. It marks a potential suspension point. The task may continue without suspension when the operation can make its result available immediately.
Does await block the thread?
Not when the task suspends correctly. The task's continuation is preserved while the thread becomes available to execute other eligible work.
Does await move code to a background thread?
No. await does not select a thread. The task continues through an executor appropriate to its isolation and execution context.
Does code after await execute immediately?
It executes only after the awaited expression has produced its value or thrown an error and the task has been scheduled to continue.
Do two await calls run concurrently?
Not merely because both use await. Two ordinary awaited calls written sequentially begin in order. Structured concurrency is required when independent operations should have overlapping lifetimes.
Can state change during await?
Yes. If the task suspends, other eligible work may run before it continues. Code should not assume that shared mutable state observed before await is unchanged afterward.
17 · TUTORIAL
Continue Learning Swift Concurrency
Read What Does async Mean in Swift? to revisit the function contract acknowledged by await.
Continue with Suspension vs Blocking in Swift to compare the two forms of waiting and see why only one releases the thread for other work.
Swift's official Concurrency documentation defines asynchronous calls and potential suspension points.
18 · TUTORIAL
Download Xcode Playground
The accompanying What Does await Mean in Swift? Xcode playground can make each possible suspension boundary visible in the console.
It should compare a synchronous wait with Task.sleep, show that code after await remains ordered, demonstrate two sequential awaited calls and allow another task to change state while the first task is suspended.
A final page should connect LaunchAPI, LaunchListFeature and a SwiftUI button so the reader can trace one task from the interface, across await, and back to visible state.
The article marks the suspension point. The playground will let the reader watch execution move around it.
