Changes in new versions / Swift 5.5

// *** before: ***
func fetchUserOld(id: Int, completion: @escaping (User?, Error?) -> Void) { }
func fetchPostsOld(user: User, completion: @escaping ([Post]?, Error?) ->
                   Void) { }

fetchUserOld(id: 1) { user, error in
    guard let user = user else { return }          // every branch must remember
    fetchPostsOld(user: user) { posts, error in    // to call the completion,
        guard let posts = posts else { return }    // and one forgotten return
        print(user, posts)                         // hangs the screen forever
    }
}

// *** in version 5.5: ***
func fetchUser(id: Intasync throws -> User { User() }
func fetchPosts(user: User) async throws -> [Post] { [] }

func load() async throws {
    let user = try await fetchUser(id: 1)
    let posts = try await fetchPosts(user: user)
    print(user, posts)
}

// await marks the place where the function may be suspended: the thread is
// given away and taken back later, and the code still reads top to bottom.
// Errors travel by the usual throws, and the result by the usual return