Async/Await — двойка ключови думи за писане на асинхронен код в синхронен стил, достъпна в Swift от iOS 13 и в Kotlin с корутини. Според Apple Swift Documentation, 2026, Async/Await заменя веригите от callback и GCD, правейки асинхронния код линеен и четивен. Ключовата дума async маркира функцията като асинхронна, а await спира нейното изпълнение до получаване на резултата.
Основно
Async/Await — езикова конструкция, която позволява да се пише асинхронен код толкова линейно, колкото синхронен. В Swift се появява в iOS 13 / macOS 10.15 заедно с рамката Swift Concurrency. В Kotlin async/await е достъпен чрез библиотеката за корутини (kotlinx.coroutines) от версия 1.3.
Преди появата на async/await асинхронният код се изграждаше върху callback, GCD или RxSwift. Веригите от вложени callback водеха до callback hell — дълбоко влагане, което затруднява четенето и отстраняването на грешки. Async/Await решава този проблем, позволявайки на асинхронните функции да „спират" в точките на await и да се възобновяват след получаване на резултата.
Според данни на Google (2025), използването на async/await в Kotlin намалява броя на редовете асинхронен код с 40–60% в сравнение с callback подхода. В Swift приемането на Swift Concurrency достигна 68% сред публикуваните приложения през 2025 г. iOS 16 добави поддръжка на Swift Concurrency в UIKit, SwiftUI и Foundation, което направи async/await стандартен инструмент за всички нови проекти на Apple.
Ключовата разлика между async/await и нишките е спирането без блокиране. Когато функцията срещне await, тя спира, освобождавайки текущата нишка за други задачи. След завършване на асинхронната операция функцията се възобновява на същата или друга нишка. Това се нарича кооперативна многозадачност.
Async функцията се компилира в state machine — краен автомат, който управлява състоянията на изпълнение. В точката на await компилаторът запазва контекста (локални променливи, адрес за връщане) и предава управлението на runtime. Когато резултатът е готов, runtime възстановява контекста и продължава изпълнението.
В Swift тази state machine е реализирана на ниво компилатор SIL (Swift Intermediate Language). В Kotlin — чрез механизма Continuation Passing Style (CPS): всяка suspend функция приема скрит параметър Continuation, чрез който се връща резултатът. И двата подхода гарантират безопасно възобновяване без изтичане на памет.
Callback подходът изисква предаване на затваряне (closure), което се извиква след завършване на операцията. При три последователни заявки се получават три нива на влагане с обработка на грешки на всяко. Async/Await превръща същите три заявки в три последователни реда с единен catch блок.
GCD (DispatchQueue) решава проблема с callback hell чрез serial опашки и DispatchGroup, но остава многословен. За проста задача — зареждане на user, след това friends, след това UI — се изисква създаване на опашки, групи и notify блокове. Async/Await изпълнява същата задача с три реда в една функция.
| Аспект | Callback | GCD | Async/Await |
|---|---|---|---|
| Влагане | Дълбоко (pyramid of doom) | Средно (групи+notify) | Линейно (flat) |
| Обработка на грешки | Per-callback | Ръчна | Единна (try/catch) |
| Отмяна | Ръчна | Ограничена | Task.cancel() |
| Thread safety | Ръчно | Чрез serial queue | Чрез MainActor |
Според данни на Apple WWDC 2024, Swift Concurrency async/await е препоръчваният подход за нов код. GCD остава за интеграция с C библиотеки и специфични low-level сценарии за обработка на нишки.
Task — основната единица асинхронна работа в Swift. Task се създава в контекста на съществуващ actor или на произволна нишка. Вътре в Task могат да се извикват async функции чрез await. TaskGroup позволява стартиране на няколко подзадачи паралелно и събиране на резултатите им — аналог на DispatchGroup на стероиди.
Actor — thread-safe reference тип, който защитава състоянието си от състезания за данни. Компилаторът гарантира, че достъпът до свойствата на actor е възможен само чрез async извиквания или в рамките на actor isolation. Actor заменя serial DispatchQueue за защита на споделено мутируемо състояние без ръчни заключвания.
Принципът на Structured Concurrency гласи: всяка асинхронна задача има родител, и родителят не приключва, докато не приключат всичките му деца. Ако задача A стартира Task { await B() }, тогава A изчаква B. Това предотвратява изтичане на задачи и осигурява предвидим lifecycle.
В Kotlin async/await е реализиран чрез coroutines — леки нишки на езика. Функцията се маркира с ключовата дума suspend (аналог на Swift async). За стартиране се използва launch (fire-and-forget) или async (с резултат). Await се извиква за получаване на резултата от async.
Dispatcher определя на кой пул от нишки се изпълнява корутината: Dispatchers.Main за UI, Dispatchers.IO за мрежа/диск и Dispatchers.Default за CPU-интензивни задачи. Structured Concurrency се осигурява чрез CoroutineScope — при отмяна на scope се отменят всички дъщерни корутини.
Според данни на JetBrains (2025), 97% от Android приложенията в Google Play използват kotlinx.coroutines, а 82% от тях прилагат async/await за мрежови заявки. Coroutines станаха де факто стандарт за асинхронно програмиране в Android. Kotlin Multiplatform също поддържа корутини, позволявайки използването на общ асинхронен код на Android, iOS и сървърната страна.
Нека разгледаме по два примера за всеки език: последователно зареждане на данни и паралелни заявки с TaskGroup или async/await.
await спира изпълнението до получаване на данните на потребителя, след това — до получаване на приятелите. Без вложени затваряния.
func loadProfile() async throws -> Profile {
let user = try await api.fetchUser()
let friends = try await api.fetchFriends(for: user.id)
return Profile(user: user, friends: friends)
}
TaskGroup стартира задачите паралелно и събира резултатите в масив. Редът на резултатите може да не съвпада с реда на стартиране.
func loadParallel() async throws -> [String] {
await withThrowingTaskGroup(of: String.self) { group in
group.addTask { try await api.fetchName() }
group.addTask { try await api.fetchEmail() }
group.addTask { try await api.fetchAvatar() }
var results = [String]()
for try await value in group { results.append(value) }
return results
}
}
suspend функциите могат да извикват други suspend функции. Аналог на Swift: последователни извиквания.
suspend fun loadProfile(): Profile {
val user = api.fetchUser()
val friends = api.fetchFriends(user.id)
return Profile(user, friends)
}
В Kotlin паралелизмът се постига чрез coroutineScope с async за всяка заявка.
suspend fun loadParallel(): List<String> = coroutineScope {
val name = async { api.fetchName() }
val email = async { api.fetchEmail() }
val avatar = async { api.fetchAvatar() }
listOf(name.await(), email.await(), avatar.await())
}
Забравеният Task { } — асинхронна функция не може да се извика от синхронен контекст без обвивка в Task. В Swift това се компилира с грешка. В Kotlin се изисква стартиране чрез lifecycleScope или viewModelScope. Опитът да се извика suspend функция от обикновена функция няма да се компилира.
Не всяка функция трябва да е асинхронна. CPU-интензивните задачи без I/O не печелят от async — по-добре се изпълняват на DispatchQueue с .userInitiated QoS. Async/Await е оптимизиран за I/O-bound операции: мрежа, диск, изчакване.
Използването на .result на главната нишка (синхронно изчакване на резултата на async функция) води до блокиране. В Swift Task.synchronousWait не се препоръчва от Apple. В Kotlin runBlocking на главната нишка е антипатърн — използвайте lifecycleScope.
Ако Task не се запази в свойство на класа, след излизане от контекста той се отменя. В Swift Task е scoped — жизненият му цикъл е обвързан със създаващия контекст. Запазвайте Task в свойство при дълготрайна операция.
Често задавани въпроси
Async/Await — езикова конструкция, която не изисква създаване на опашки. DispatchQueue — системен API за управление на нишки. async/await се компилира в state machine на ниво език, GCD — в syscall на ниво операционна система.
Не — await спира функцията, но освобождава нишката за други задачи. След завършване на операцията функцията се възобновява на която и да е достъпна нишка от пула.
Да — чрез Task.cancel() в Swift или Job.cancel() в Kotlin. Отмяната се разпространява върху всички подзадачи според Structured Concurrency. Кодът сам проверява флага за отмяна чрез Task.isCancelled или ensureActive().
MainActor — actor, чиито свойства и методи винаги се изпълняват на главната нишка. Маркирайки функция с @MainActor, гарантирате, че UI актуализациите се случват на правилната нишка, заменяйки DispatchQueue.main.async.
Async/Await е достъпен в Swift (iOS 13+), Kotlin (чрез kotlinx.coroutines), Dart (Flutter), JavaScript/TypeScript, Python, C#, Rust и Go (чрез goroutines със синтактична захар).
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също