Using Async/Await
While promise chaining with .then()
is powerful, Then
also provides an async
/await
style of programming that allows you to write asynchronous code as if it were synchronous. This can make complex sequences of operations even easier to read and reason about.
awaitPromise()
The awaitPromise()
function is the core of this feature. It takes a Promise
as input and synchronously waits for it to complete.
- If the promise succeeds,
awaitPromise()
returns its result. - If the promise fails,
awaitPromise()
throws the error.
// This will block the current thread until the promise resolves
let userId = try awaitPromise(fetchUserId())
print(userId) // 1234
Warning: Because awaitPromise
is blocking, you should almost never call it directly on the main thread, as it will freeze your UI.
async
Block
To use awaitPromise
safely without blocking the main thread, you wrap your code in an async
block. The async
function executes its closure on a background thread and wraps it in a Promise
. This allows you to use try awaitPromise()
inside, and any errors thrown will be caught by the returned promise.
This combination lets you write asynchronous code that looks synchronous.
async {
// This code runs on a background thread
let userId = try awaitPromise(fetchUserId())
let userName = try awaitPromise(fetchUserNameFromId(userId))
let isFollowed = try awaitPromise(fetchUserFollowStatusFromName(userName))
return isFollowed
}.then { isFollowed in
// Back on the original thread
print("Follow status: \(isFollowed)")
}.onError { e in
// Handle any error from the async block
print("An error occurred: \(e)")
}
Notice we don't need try!
inside the async
block because it automatically catches errors and rejects its promise.
Await Operators
To make the syntax even more concise, Then
provides prefix operators as shorthands for awaitPromise
.
..
(Await)
The ..
operator is a direct replacement for try awaitPromise()
.
// Before
let userId = try awaitPromise(fetchUserId())
// After
let userId = try ..fetchUserId()
..?
(Optional Await)
The ..?
operator is a failable version of ..
. Instead of throwing an error, it returns nil
if the promise fails. This is useful when a failed operation is not critical.
async {
// If fetchFriends fails, friends will be nil instead of throwing an error.
let friends = ..?fetchFriends()
print(friends?.count ?? 0)
}
These operators also work with optional promises (Promise<T>?
). If the promise itself is nil
, ..
will throw an unwrappingFailed
error, while ..?
will simply return nil
.