Как запустить две задачи параллельно и после их завершения выполнить третью в Swift
Что проверяют
Умеете ли вы выразить зависимость «третья задача после двух» без гонок и блокировок, причём в двух моделях: GCD и Swift Concurrency. Следом почти всегда спрашивают, что будет, если одна из задач упадёт с ошибкой, и как отменить оставшуюся.
Как отвечать
В современном коде первым называйте async let:
async let user = api.loadUser()
async let orders = api.loadOrders()
let (u, o) = try await (user, orders)
showProfile(u, o)Обе дочерние задачи стартуют сразу, а await ждёт обе. Если одна бросает ошибку, та пробрасывается наружу, а незавершённая дочерняя задача получает отмену при выходе из области видимости. Отмена кооперативная: код внутри должен проверять Task.isCancelled или вызывать try Task.checkCancellation().
Другие инструменты:
withTaskGroupиwithThrowingTaskGroup: когда число задач заранее неизвестно, например загрузка массива картинок;DispatchGroup: для кода на колбэках.enter()перед каждой задачей,leave()в её завершении,notify(queue: .main)для третьей;OperationQueue: третья операция получаетaddDependencyна две первые, плюс есть отмена иmaxConcurrentOperationCount;- Combine:
Publishers.Zipдля двух издателей, если проект уже на нём.
Уточните разницу между конкурентностью и параллелизмом: задачи идут на кооперативном пуле потоков и одновременно выполнятся на разных ядрах, только если есть свободные потоки и работа не держит их блокирующими вызовами.
Где спотыкаются
Пишут let a = await loadA() и следом let b = await loadB(). Это последовательное выполнение, вторая загрузка стартует только после первой.
Забывают leave() в ветке ошибки. notify не срабатывает никогда, а лишний leave() роняет приложение.
Вызывают group.wait() на главном потоке или sleep внутри задач Swift Concurrency. Первое замораживает интерфейс, второе занимает поток кооперативного пула, а их там примерно столько, сколько ядер.