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.