【Swift Concurrency】深入理解 `async let` 与 `TaskGroup`:并发任务的生命周期与错误传播机制

一、什么是 async let

async let 是 Swift 提供的一种结构化并发语法糖,用于并发地启动多个子任务,并延迟等待其结果。

✅ 基本用法

swift 复制代码
func fetchData() async {
    async let first = fetchPart1()
    async let second = fetchPart2()
    async let third = fetchPart3()

    let result = await (first, second, third)
    print(result)
}
  • 每个 async let 会创建一个子任务(child task)。
  • 子任务立即开始执行,不会阻塞当前任务。
  • 使用 await 获取结果时,按顺序等待(从左到右)。

✅ 支持任意表达式

swift 复制代码
async let number = 123
async let str = "Hello"

实际上,Swift 会将这些表达式包装为 ChildTask,并在后台并发执行。

二、什么是 TaskGroup?

TaskGroup 是 Swift 提供的动态并发任务创建机制,适合任务数量在运行时决定的场景。

✅ 示例:动态创建任务

swift 复制代码
func fetchData(count: Int) async {
    var results = [String]()

    await withTaskGroup(of: String.self) { group in
        for index in 0..<count {
            group.addTask {
                await self.fetchPart(index)
            }
        }

        for await result in group {
            results.append(result)
        }
    }

    print(results)
}
  • addTask 动态添加子任务。
  • for await 按完成顺序处理结果(谁先完成谁先处理)。
  • 也可以使用 group.next() 手动迭代。

三、生命周期机制对比

特性 async let TaskGroup
生命周期作用域 当前局部作用域(函数、do块等) withTaskGroup 闭包
正常退出时 自动取消并等待所有子任务 自动等待所有子任务(不取消)
异常退出时 自动取消并等待所有子任务 自动取消并等待所有子任务
是否支持动态任务数 ❌ 不支持 ✅ 支持
是否按完成顺序处理结果 ❌ 按声明顺序 await ✅ 按完成顺序

四、错误传播机制详解

async let 的错误传播

swift 复制代码
async let f = fast()
async let s = slow()

do {
    try await (f, s)
} catch {
    print("Caught error: \(error)")
}
  • 错误传播顺序取决于 await 的顺序。
  • 如果 fast() 先抛出错误,slow() 会被隐式取消并等待。
  • 如果 slow() 先完成但 fast() 先抛出错误,slow() 的错误不会被捕获。

✅ TaskGroup 的错误传播

swift 复制代码
try await withThrowingTaskGroup(of: Void.self) { group in
    group.addTask { try await fast() }
    group.addTask { try await slow() }

    for try await _ in group {
        // 处理结果
    }
}
  • 使用 for try await 时,第一个抛出的错误会立即传播。
  • 其余任务会被取消并等待。
  • 错误传播顺序与任务完成顺序无关,谁先抛谁传播。

五、实战建议:如何选择?

场景 推荐方式 原因
任务数量固定 async let 简洁、语法糖优雅
任务数量动态 TaskGroup 支持运行时添加任务
需要"fail fast" TaskGroup 错误传播更及时、可控
需要按顺序处理结果 async let 可控制 await 顺序
需要按完成顺序处理结果 TaskGroup AsyncSequence 自动支持

六、常见误区与注意事项

⚠️ 1. async let 不支持逃逸闭包

swift 复制代码
async let result = someAsyncFunc()
DispatchQueue.main.async {
    // ❌ 不能使用 result,生命周期已结束
}

⚠️ 2. 不 await 也会等待完成

swift 复制代码
async let f = slowTask()
print("End") // 仍会等待 f 完成后再退出作用域

⚠️ 3. 不支持"真正的 fire-and-forget"

  • 结构化任务总是会被等待。
  • 若需真正"放飞自我",请使用非结构化 Task:
swift 复制代码
Task.detached {
    await someAsyncFunc()
}

七、总结:一句话记住差异

async let 是固定并发任务的语法糖,生命周期绑定作用域,错误传播顺序依赖 await 顺序;

TaskGroup 是动态并发任务的管理器,生命周期绑定闭包,错误传播顺序更及时、可控。

相关推荐
5***790015 小时前
Swift进阶
开发语言·ios·swift
大炮走火1 天前
iOS在制作framework时,oc与swift混编的流程及坑点!
开发语言·ios·swift
0***141 天前
Swift资源
开发语言·ios·swift
z***I3941 天前
Swift Tips
开发语言·ios·swift
J***Q2921 天前
Swift Solutions
开发语言·ios·swift
Gavin-Wang1 天前
Swift + CADisplayLink 弱引用代理(Proxy 模式) 里的陷阱
开发语言·ios·swift
非专业程序员2 天前
Rust RefCell 多线程读为什么也panic了?
rust·swift
胎粉仔3 天前
Swift 初阶 —— Sendable 协议 & data races
开发语言·ios·swift·sendable·并发域·data races
m0_495562783 天前
Swift-Enum
java·算法·swift
m0_495562783 天前
Swift-snapKit使用
开发语言·elasticsearch·swift