随着 Swift 5.5 的发布,Swift 引入了全新的结构化并发 (Structured Concurrency) 模型。这套模型基于 async/await,彻底改变了我们编写异步代码的方式,告别了传统闭包回调 (Callback) 带来的“毁灭金字塔 (Pyramid of Doom)”。

为什么需要结构化并发?

在结构化并发之前,如果我们有多个异步任务,通常会写出这样的代码:

func fetchUserData(completion: @escaping (User?, Error?) -> Void) {
    URLSession.shared.dataTask(with: userURL) { data, response, error in
        guard let data = data else {
            completion(nil, error)
            return
        }
        do {
            let user = try JSONDecoder().decode(User.self, from: data)
            completion(user, nil)
        } catch {
            completion(nil, error)
        }
    }.resume()
}

这存在几个显著问题:

  1. 控制流混乱:极易遗漏 completion 调用,导致程序挂起。
  2. 错误处理繁琐:无法直接使用 Swift 的 throw 机制,必须通过 Result 或双可选值。
  3. 任务无法自动取消:如果用户退出了页面,底层的网络请求仍然在消耗带宽和电量。

Swift 中的 Async/Await

async/await 让异步代码的编写和同步代码一样自然:

func fetchUserData() async throws -> User {
    let (data, _) = try await URLSession.shared.data(from: userURL)
    return try JSONDecoder().decode(User.self, from: data)
}

这里 await 明确指出了挂起点 (Suspension Point)。当代码执行到这里时,当前线程会被释放去执行其他任务,直到异步网络请求完成后,再由系统调度恢复执行。

Task 与结构化作用域

结构化并发的核心在于生命周期绑定。子任务的生命周期应当与父任务绑定,这通过 TaskTaskGroup 实现。

1. 异步绑定 (Async Let)

当你需要并行执行多个任务,且需要等待它们全部完成时,使用 async let 是最简单的方式:

func fetchDashboardData() async throws -> Dashboard {
    async let user = fetchUserData()
    async let stats = fetchAppStats()
    
    // 两者并行执行,在此处汇合等待
    return try await Dashboard(user: user, stats: stats)
}

2. 任务组 (Task Group)

对于动态数量的任务,我们应当使用 withThrowingTaskGroup

func fetchAllImages(urls: [URL]) async throws -> [UIImage] {
    try await withThrowingTaskGroup(of: UIImage.self) { group in
        for url in urls {
            group.addTask {
                let (data, _) = try await URLSession.shared.data(from: url)
                return UIImage(data: data) ?? UIImage()
            }
        }
        
        var images = [UIImage]()
        for try await image in group {
            images.append(image)
        }
        return images
    }
}

自动取消机制 (Automatic Cancellation)

结构化并发最大的优势之一是自动取消传播。如果父任务被取消,所有的子任务(包括 TaskGroup 中的任务)都会自动接收到取消信号并停止执行:

group.addTask {
    // 定期检查取消状态
    try Task.checkCancellation()
    let (data, _) = try await URLSession.shared.data(from: url)
    return UIImage(data: data) ?? UIImage()
}

总结

Swift 结构化并发不仅让代码更加整洁,也让内存安全和资源管理提升到了编译器级别。作为 iOS 开发者,熟练掌握并全面转向 async/await 能够极大地减少 App 里的异步 Bug,让代码更具可维护性。