SC #11:任务组

发布: (2026年1月20日 GMT+8 06:27)
4 min read
原文: Dev.to

I’m happy to translate the article for you, but I’ll need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have it, I’ll keep the source link at the top and translate the rest into Simplified Chinese while preserving all formatting, markdown, and code blocks.

TaskGroup

TaskGroup 包含动态创建的子任务,可以串行或并发执行。只有当 所有 子任务都完成时,组才被视为已结束。

withTaskGroup(of:returning:isolation:body:) 允许创建上下文,以使用 addTask(priority:operation:) 添加子任务。

基本示例:下载图片

func downloadPhoto(url: URL) async -> UIImage {
    // …
}

await withTaskGroup(of: UIImage.self) { taskGroup in
    let photoURLs = await listPhotoURLs(inGallery: "Vacaciones")
    for photoURL in photoURLs {
        taskGroup.addTask {
            await downloadPhoto(url: photoURL)
        }
    }
}

TaskGroup 的行为类似于 forEach,并发执行每个子任务并收集结果。

收集结果

作为集合

可以将返回类型定义为集合(例如 [UIImage].self)。在启动所有子任务后,使用组的 AsyncSequence 来等待结果并构建最终的集合。

let images: [UIImage] = await withTaskGroup(
    of: UIImage.self,
    returning: [UIImage].self
) { taskGroup in
    let photoURLs = await listPhotoURLs(inGallery: "Vacaciones")
    for photoURL in photoURLs {
        taskGroup.addTask {
            await downloadPhoto(url: photoURL)
        }
    }

    var images = [UIImage]()
    for await result in taskGroup {
        images.append(result)
    }
    return images
}

使用 reduce

由于 TaskGroup 符合 AsyncSequence,也可以使用 reduce 运算符:

await withTaskGroup(of: UIImage.self, returning: [UIImage].self) { taskGroup in
    let photoURLs = try! await listPhotoURLs(inGallery: "Vacaciones")

    for photoURL in photoURLs {
        taskGroup.addTask {
            try! await downloadPhoto(url: photoURL)
        }
    }

    return await taskGroup.reduce(into: [UIImage]()) { partialResult, image in
        partialResult.append(image)
    }
}

Source:

ThrowingTaskGroup

当某个子任务可能抛出错误时,使用 withThrowingTaskGroup(of:returning:isolation:body:)

try await withThrowingTaskGroup(of: UIImage.self, returning: [UIImage].self) { taskGroup in
    let photoURLs = try await listPhotoURLs(inGallery: "Vacaciones")

    for photoURL in photoURLs {
        taskGroup.addTask {
            try await downloadPhoto(url: photoURL)
        }
    }

    return try await taskGroup.reduce(into: [UIImage]()) { partialResult, image in
        partialResult.append(image)
    }
}

错误行为

  • ThrowingTaskGroup 不会在子任务抛出异常时自动失败;子任务会被标记为失败,但组会继续执行。
  • 要传播错误并取消剩余任务,需要使用 group.next() 显式地获取每个子任务的结果。
try await withThrowingTaskGroup(of: Void.self) { group in
    group.addTask { throw SomeError() }
    // 这里组不会失败
}
try await withThrowingTaskGroup(of: Void.self) { group in
    group.addTask { throw SomeError() }
    try await group.next()   // 传播错误并取消正在进行的任务
}

next()

group.next() 逐个返回子任务的结果,顺序是随机的,如果子任务失败则抛出相应的错误。这使得可以单独处理每个错误。

while let result = try await taskGroup.next() {
    print("Got a new result:", result)
}

任务取消

  • 可以使用 group.cancelAll() 取消整个任务组。
  • 如果通过 addTask(priority:operation:) 向已取消的组添加任务,该任务会在创建后立即被取消。若想防止任务甚至开始执行,请使用 addTaskUnlessCancelled(priority:operation:)
for photoURL in photoURLs {
    let didAddTask = taskGroup.addTaskUnlessCancelled {
        try await downloadPhoto(url: photoURL)
    }
    print("Added task: \(didAddTask)")
}
Back to Blog

相关文章

阅读更多 »

SC #8:取消 Task

Swift 和 SwiftUI 中的 Task 取消 > 注意:在 Swift 中,取消 Task 并不保证执行会立即停止。每个 Task 必须检查更多…

SC #10:解耦任务

一个脱耦的 Detached Task 以异步方式执行操作,脱离了包裹它的结构化并发上下文。不要继承这个 c...