async/await
Tags: asyncawaitswift
Published: 2024-04-11 08:26:14

---
title: async/await
published: true
date: 2024-02-28 18:17:12 UTC
tags: asyncawaitswift
canonical_url: https://wesleydegroot.nl/blog/post/async-await
---

Let's delve into the fascinating world of **Swift's `async/await`**.  
This powerful feature, introduced in Swift 5.5, revolutionizes asynchronous programming, making it more intuitive and readable. Buckle up as we explore the ins and outs of `async/await`!

## What is `async/await`?

At its core, `async/await` simplifies handling asynchronous tasks. It allows you to write asynchronous code that looks almost synchronous. Here's how it works:

1. `async` Functions: You mark a function as `async`. Inside this function, you can use `await` to pause execution until an asynchronous operation completes.

2. `await` Expression: When you encounter an `await` expression, the function suspends execution until the awaited task finishes. Meanwhile, other tasks can continue running concurrently.

## Example 1: Fetching Data

Let's start with a common scenario: fetching data from a remote API. Imagine we have a callback-based function for fetching data:

```swift
func fetchData(completion: @escaping (Data?, Error?) -> Void) {
    let url = URL(string: "https://api.example.com/data")!
    URLSession.shared.dataTask(with: url) { data, _, error in
        completion(data, error)
    }.resume()
}
```

Now, let's convert this to an `async` function using `async/await`:

```swift
func fetchData() async throws -> Data {
    let url = URL(string: "https://api.example.com/data")!
    return try await URLSession.shared.data(from: url).0
}
```

In the `async` version:

- We use `try await` to wait for the data to be fetched.
- The error handling is cleaner, thanks to Swift's `throws`.

## Example 2: Multiple API Calls

Imagine we have a callback-based function for fetching data:

```swift
func fetchUser(id: Int, completion: @escaping (User?, Error?) -> Void) {
    // Fetch user details...
}

func fetchPosts(for user: User, completion: @escaping ([Post]?, Error?) -> Void) {
    // Fetch posts for the user...
}

// Usage:

fetchUser(id: 1) { user, error in
  guard let user = user else {
    print(error)
  }
  // Guard for no error
  self.fetchPosts(for: user) { posts, error in
    // Process user and posts
  }
}
```

Suppose we need to fetch a user and then fetch their posts. In the callback-based world, this can get messy. But with `async/await`, it's elegant:

```swift
func fetchUser(id: Int) async throws -> User {
    // Fetch user details...
}

func fetchPosts(for user: User) async throws -> [Post] {
    // Fetch posts for the user...
}

// Usage
Task {
    do {
        let user = try await fetchUser(id: 1)
        let posts = try await fetchPosts(for: user)
        // Process user and posts
    } catch {
        // Handle errors
    }
}
```

## Conclusion

By embracing `async/await`, you create a better development experience and write more efficient code. Say goodbye to callback hell and welcome a cleaner, more expressive way of handling asynchronous tasks in Swift. 🚀

Resources [https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html#async-await](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html#async-await)