SwiftSeniorTechnical
Что такое Task в Swift Concurrency и что такое TaskGroup?
Task запускает одну асинхронную задачу с возможностью отмены через task.cancel() и проверкой Task.isCancelled. TaskGroup (withTaskGroup/withThrowingTaskGroup) запускает N параллельных задач и собирает результаты через for await in group.
Task и TaskGroup в Swift Concurrency
Task и TaskGroup — основные примитивы структурированного параллелизма в Swift, введённые в Swift 5.5 (iOS 15+).
Task — одиночная асинхронная задача
// Неструктурированная Task — запускается немедленно
let task = Task {
let result = await fetchData()
print(result)
}
// Ожидание результата
let value = await task.value
// Отмена
task.cancel()
Типы Task
// Task с возвратом значения
let task = Task<String, Error> {
try await fetchString()
}
let result = try await task.value
// Task с приоритетом
let backgroundTask = Task(priority: .background) {
await processLargeDataset()
}
// Task.detached — не наследует actor context и приоритет
let detached = Task.detached(priority: .low) {
// НЕ наследует @MainActor даже если вызван с главного актора
await backgroundWork()
}
Отмена задач
@MainActor
class SearchViewModel: ObservableObject {
@Published var results: [String] = []
private var searchTask: Task<Void, Never>?
func search(query: String) {
searchTask?.cancel() // отменяем предыдущий поиск
searchTask = Task {
do {
try Task.checkCancellation() // ранняя проверка
let data = try await searchAPI(query: query)
try Task.checkCancellation() // после долгой операции
self.results = data
} catch is CancellationError {
// нормальная отмена, не логируем
} catch {
print("Search failed:", error)
}
}
}
deinit { searchTask?.cancel() }
}
TaskGroup — параллельные задачи
TaskGroup позволяет запускать несколько задач параллельно и собирать результаты.
// withTaskGroup — без throws
func fetchAllUsers(ids: [UUID]) async -> [User] {
await withTaskGroup(of: User?.self) { group in
for id in ids {
group.addTask {
try? await fetchUser(id: id) // ошибки игнорируются
}
}
var users: [User] = []
for await user in group {
if let user { users.append(user) }
}
return users
}
}
// withThrowingTaskGroup — с propagation ошибок
func fetchRequiredUsers(ids: [UUID]) async throws -> [User] {
try await withThrowingTaskGroup(of: User.self) { group in
for id in ids {
group.addTask { try await fetchUser(id: id) }
}
var users: [User] = []
for try await user in group { // первая ошибка отменяет группу
users.append(user)
}
return users
}
}
Ограничение параллелизма
// Максимум N параллельных задач
func processWithLimit<T>(_ items: [T], maxConcurrency: Int, work: @escaping (T) async throws -> Void) async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
var activeTasks = 0
for item in items {
if activeTasks >= maxConcurrency {
try await group.next() // ждём завершения одной задачи
activeTasks -= 1
}
group.addTask { try await work(item) }
activeTasks += 1
}
try await group.waitForAll()
}
}
// Использование: не более 3 параллельных загрузок
try await processWithLimit(urls, maxConcurrency: 3) { url in
await downloadImage(from: url)
}
Подводные камни
Task.detachedне наследует actor context — вызов UI-обновлений из detached task безawait MainActor.runвызывает ошибки в Swift 6.- Отмена TaskGroup отменяет все дочерние задачи, но не ждёт их завершения — результаты уже начатых задач теряются.
Task.checkCancellation()бросаетCancellationError— не забывайте обрабатывать его отдельно от других ошибок.- Создание тысяч
Task {}без ограничения параллелизма исчерпывает thread pool — используйте TaskGroup с ограничением. - В
withThrowingTaskGroupпервая ошибка изfor try awaitотменяет остальные задачи — если нужны все результаты, собирайте ошибки вResult. - Task не сохраняет приоритет при передаче через actor boundary — используйте явный
priority:. - iOS 15 minimum target для async/await — на iOS 13-14 нужен
withCheckedContinuationдля обёртки completion-based API. - ViewModel с
@MainActorи TaskGroup: дочерние задачи в группе не наследуют MainActor — UI обновления нужно явно переносить черезawait MainActor.run.
Common mistakes
- Сводить «
Taskв Swift Concurrency и что такоеTaskGroup» к синтаксису и не объяснять ARC. - Игнорировать жизненный цикл, основной поток или момент освобождения ресурсов в сценарии swift-16.
- Выбирать API по привычке, не проверяя состояние, ошибки, доступность и платформенные ограничения.
What the interviewer is testing
- Формулирует точную модель для «
Taskв Swift Concurrency и что такоеTaskGroup» и подтверждает ее корректным примером. - Умеет связать ответ с Swift Concurrency, тестированием и отладкой на устройстве.
- Называет ограничения подхода swift-16, включая производительность, память и сопровождение.