Async functions declaration in Swift - Time & Space Complexity
When we declare async functions, we want to know how their execution time changes as we call them with different inputs.
We ask: how does the time to complete grow when the input size grows?
Analyze the time complexity of the following async function declaration and usage.
func fetchData(for ids: [Int]) async -> [String] {
var results = [String]()
for id in ids {
let data = await fetchSingleData(id: id)
results.append(data)
}
return results
}
func fetchSingleData(id: Int) async -> String {
// Simulates network call
return "Data for \(id)"
}
This code fetches data asynchronously for each id in the input list one by one.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The for-loop that calls
fetchSingleDatafor each id. - How many times: Once for each element in the input array
ids.
Each id causes one async call, so the total work grows as the number of ids grows.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 async calls |
| 100 | 100 async calls |
| 1000 | 1000 async calls |
Pattern observation: The total time grows roughly in direct proportion to the number of items.
Time Complexity: O(n)
This means the time to finish grows linearly with the number of items you fetch.
[X] Wrong: "Async functions always run instantly or in constant time."
[OK] Correct: Even though async functions let other work happen while waiting, the total time still depends on how many times you call them and what each call does.
Understanding how async functions scale with input size helps you write efficient code and explain your reasoning clearly in real projects or interviews.
What if we changed the for-loop to run all fetches concurrently using async let? How would the time complexity change?
Practice
async keyword do when declaring a function in Swift?Solution
Step 1: Understand the purpose of
Theasyncasynckeyword tells Swift that the function can pause its work to wait for other tasks without blocking the whole program.Step 2: Compare with other options
The other options describe behaviors thatasyncdoes not do: it does not automatically use multiple threads, does not restrict to main thread, and does not block execution.Final Answer:
It marks the function as able to pause and wait for asynchronous tasks. -> Option AQuick Check:
async means function can pause and wait [OK]
- Thinking async makes function run faster automatically
- Confusing async with threading
- Assuming async blocks the main thread
Solution
Step 1: Recall Swift async function syntax
In Swift, theasynckeyword comes after the function parentheses and before anythrowskeyword.Step 2: Check each option
The correct syntax isfunc fetchData() async throws -> String {}. The other options placeasyncincorrectly (beforefunc, betweenfuncand name, or afterthrows).Final Answer:
func fetchData() async throws -> String {} -> Option DQuick Check:
async goes after () and before throws [OK]
- Placing async before func keyword
- Putting async after throws
- Mixing order of async and throws
func greet() async -> String {
return "Hello"
}
func printGreeting() async {
let message = await greet()
print(message)
}
Task {
await printGreeting()
}Solution
Step 1: Analyze async calls and awaits
The functiongreet()is async and returns "Hello".printGreeting()awaitsgreet()correctly and prints the message.Step 2: Check how printGreeting is called
TheTaskblock callsawait printGreeting(), so the print happens inside the asynchronous context.Final Answer:
Hello -> Option BQuick Check:
Await async function inside Task prints output [OK]
- Forgetting to await greet() inside printGreeting()
- Not calling printGreeting() inside Task
- Expecting output without running async context
func loadData() throws async -> String {
return "Data"
}Solution
Step 1: Check correct order of async and throws
In Swift,asyncmust come beforethrowsin the function declaration.Step 2: Verify other options
Missingawaitkeyword inside the function body. is incorrect becauseawaitis only needed when calling other async functions. Function must returnVoidif marked async. is wrong; async functions can return any type. Cannot combinethrowswithasync. is false; async and throws can be combined.Final Answer:
The order ofthrowsandasyncis incorrect. -> Option AQuick Check:
async comes before throws in declaration [OK]
- Swapping async and throws order
- Expecting await inside function without async calls
- Thinking async functions must return Void
fetchUsernames() that calls two async functions fetchFromServer() and fetchFromCache() and returns the first non-empty result. Which is the correct way to declare and use fetchUsernames()?Solution
Step 1: Check async and throws order and usage
The function calls two async throwing functions, so it must be declaredasync throwswithasyncbeforethrows. Calls to async throwing functions requiretry await.Step 2: Analyze each option
func fetchUsernames() async -> [String] { if let server = await fetchFromServer(), !server.isEmpty { return server } let cache = await fetchFromCache() return cache } missesthrowsandtry. async func fetchUsernames() -> [String] { let server = fetchFromServer() if !server.isEmpty { return server } return fetchFromCache() } has wrong syntax placingasyncbeforefuncand missing awaits. func fetchUsernames() throws async -> [String] { let server = try fetchFromServer() if !server.isEmpty { return server } return try fetchFromCache() } swapsthrowsandasyncand missesawait. Only func fetchUsernames() async throws -> [String] { let server = try await fetchFromServer() if !server.isEmpty { return server } return try await fetchFromCache() } correctly declares and usestry await.Final Answer:
func fetchUsernames() async throws -> [String] { ... } -> Option CQuick Check:
async before throws, use try await calls [OK]
- Placing async before func keyword
- Swapping async and throws order
- Forgetting try with await on throwing async calls
Ready to prove it?
Pass the test and this pattern moves unknown
