Async/Await est une paire de mots-clés pour écrire du code asynchrone dans un style synchrone, disponible en Swift depuis iOS 13 et en Kotlin avec les coroutines. Selon Apple Swift Documentation, 2026, Async/Await remplace les chaînes de callbacks et GCD, rendant le code asynchrone linéaire et lisible. Le mot-clé async marque une fonction comme asynchrone, et await suspend son exécution jusqu'à la réception du résultat.
Points Clés
Async/Await est une construction de langage qui permet d'écrire du code asynchrone aussi linéairement que le code synchrone. Dans Swift, il est apparu dans iOS 13 / macOS 10.15 avec le framework Swift Concurrency. Dans Kotlin, async/await est disponible via la bibliothèque de coroutines (kotlinx.coroutines) depuis la version 1.3.
Avant async/await, le code asynchrone était construit sur des callbacks, GCD ou RxSwift. Les chaînes de callbacks imbriqués menaient à l'enfer des callbacks — une imbrication profonde qui complique la lecture et le débogage. Async/Await résout ce problème en permettant aux fonctions asynchrones de « suspendre » aux points await et de reprendre après réception du résultat.
Selon Google (2025), l'utilisation d'async/await dans Kotlin réduit le nombre de lignes de code asynchrone de 40 à 60 % par rapport à l'approche par callbacks. Dans Swift, l'adoption de Swift Concurrency a atteint 68 % parmi les applications publiées en 2025. iOS 16 a ajouté la prise en charge de Swift Concurrency dans UIKit, SwiftUI et Foundation, faisant d'async/await l'outil standard pour tous les nouveaux projets Apple.
La principale différence entre async/await et les threads est la suspension sans blocage. Lorsqu'une fonction rencontre await, elle se suspend, libérant le thread actuel pour d'autres tâches. Une fois l'opération asynchrone terminée, la fonction reprend sur le même thread ou sur un autre. C'est ce qu'on appelle le multitâche coopératif.
Une fonction async est compilée en une machine d'états — un automate fini qui gère les états d'exécution. Au point await, le compilateur sauvegarde le contexte (variables locales, adresse de retour) et donne le contrôle au runtime. Lorsque le résultat est prêt, le runtime restaure le contexte et continue l'exécution.
Dans Swift, cette machine d'états est implémentée au niveau du compilateur SIL (Swift Intermediate Language). Dans Kotlin, le mécanisme Continuation Passing Style (CPS) est utilisé : chaque fonction suspend accepte un paramètre Continuation caché par lequel le résultat est retourné. Les deux approches garantissent une reprise sécurisée sans fuites mémoire.
L'approche par callbacks nécessite de passer une closure qui est appelée après la fin de l'opération. Avec trois requêtes séquentielles, on obtient trois niveaux d'imbrication avec gestion d'erreurs à chaque niveau. Async/Await transforme ces trois mêmes requêtes en trois lignes séquentielles avec un seul bloc catch.
GCD (DispatchQueue) résout l'enfer des callbacks via des files d'attente séries et DispatchGroup, mais reste verbeux. Pour une tâche simple — charger un utilisateur, puis les amis, puis l'interface — il faut créer des files, des groupes et des blocs notify. Async/Await effectue la même tâche avec trois lignes dans une seule fonction.
| Aspect | Callback | GCD | Async/Await |
|---|---|---|---|
| Imbrication | Profonde (pyramide de la perdition) | Moyenne (groupes+notify) | Linéaire (plate) |
| Gestion des erreurs | Par callback | Manuelle | Unifiée (try/catch) |
| Annulation | Manuelle | Limitée | Task.cancel() |
| Sécurité des threads | Manuelle | Via file série | Via MainActor |
Selon Apple WWDC 2024, Swift Concurrency async/await est l'approche recommandée pour le nouveau code. GCD reste pour l'intégration avec les bibliothèques C et les scénarios spécifiques de bas niveau de gestion des threads.
Task est l'unité de base du travail asynchrone dans Swift. Une Task est créée dans le contexte d'un acteur existant ou sur un thread arbitraire. À l'intérieur d'une Task, on peut appeler des fonctions async via await. TaskGroup permet de lancer plusieurs tâches enfants en parallèle et de collecter leurs résultats — un analogue de DispatchGroup amélioré.
Actor est un type de référence thread-safe qui protège son état des courses de données. Le compilateur garantit que l'accès aux propriétés de l'acteur n'est possible que par des appels async ou dans l'isolement de l'acteur. Actor remplace la DispatchQueue série pour protéger l'état mutable partagé sans verrous manuels.
Le principe de Structured Concurrency énonce : chaque tâche asynchrone a un parent, et le parent ne se termine pas tant que tous ses enfants ne sont pas terminés. Si la tâche A lance Task { await B() }, alors A attend B. Cela empêche les fuites de tâches et garantit un cycle de vie prévisible.
Dans Kotlin, async/await est implémenté via les coroutines — des threads légers du langage. Une fonction est marquée avec le mot-clé suspend (analogue à async de Swift). Pour lancer, on utilise launch (fire-and-forget) ou async (avec résultat). Await est appelé pour obtenir le résultat d'async.
Dispatcher détermine sur quel pool de threads la coroutine s'exécute : Dispatchers.Main pour l'interface, Dispatchers.IO pour le réseau/disque et Dispatchers.Default pour les tâches intensives en CPU. Structured Concurrency est garantie via CoroutineScope — lorsque le scope est annulé, toutes les coroutines enfants sont annulées.
Selon JetBrains (2025), 97 % des applications Android sur Google Play utilisent kotlinx.coroutines, et 82 % d'entre elles utilisent async/await pour les requêtes réseau. Les coroutines sont devenues le standard de facto pour la programmation asynchrone sur Android. Kotlin Multiplatform prend également en charge les coroutines, permettant d'utiliser du code asynchrone partagé sur Android, iOS et le serveur.
Regardons deux exemples pour chaque langage : chargement séquentiel de données et requêtes parallèles avec TaskGroup ou async/await.
await suspend l'exécution jusqu'à la réception des données utilisateur, puis jusqu'à la réception des amis. Aucune closure imbriquée.
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 lance des tâches en parallèle et collecte les résultats dans un tableau. L'ordre des résultats peut ne pas correspondre à l'ordre de lancement.
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
}
}
Les fonctions suspend peuvent appeler d'autres fonctions suspend. Analogue à Swift : appels séquentiels.
suspend fun loadProfile(): Profile {
val user = api.fetchUser()
val friends = api.fetchFriends(user.id)
return Profile(user, friends)
}
Dans Kotlin, le parallélisme est réalisé via coroutineScope avec async pour chaque requête.
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 { } oubliée — une fonction async ne peut pas être appelée depuis un contexte synchrone sans être enveloppée dans une Task. Dans Swift, cela génère une erreur de compilation. Dans Kotlin, cela nécessite un lancement via lifecycleScope ou viewModelScope. Essayer d'appeler une fonction suspend depuis une fonction normale ne compilera pas.
Toutes les fonctions n'ont pas besoin d'être asynchrones. Les tâches intensives en CPU sans E/S ne bénéficient pas d'async — il est préférable de les exécuter sur DispatchQueue avec QoS .userInitiated. Async/Await est optimisé pour les opérations liées aux E/S : réseau, disque, attente.
Utiliser .result sur le thread principal (attente synchrone du résultat d'une fonction async) entraîne un blocage. Dans Swift, Task.synchronousWait n'est pas recommandé par Apple. Dans Kotlin, runBlocking sur le thread principal est un anti-patron — utilisez lifecycleScope.
Si une Task n'est pas stockée dans une propriété de classe, après avoir quitté le contexte, elle est annulée. Dans Swift, Task est limitée par portée — son cycle de vie est lié au contexte créateur. Stockez la Task dans une propriété si vous avez besoin d'une opération de longue durée.
Foire Aux Questions
Async/Await est une construction de langage qui ne nécessite pas de créer des files d'attente. DispatchQueue est une API système pour gérer les threads. Async/await est compilé en une machine d'états au niveau du langage, GCD — en syscalls au niveau de l'OS.
Non — await suspend la fonction mais libère le thread pour d'autres tâches. Une fois l'opération terminée, la fonction reprend sur n'importe quel thread disponible du pool.
Oui — via Task.cancel() dans Swift ou Job.cancel() dans Kotlin. L'annulation se propage à toutes les tâches enfants selon Structured Concurrency. Le code vérifie le drapeau d'annulation via Task.isCancelled ou ensureActive().
MainActor est un acteur dont les propriétés et méthodes s'exécutent toujours sur le thread principal. En marquant une fonction avec @MainActor, vous garantissez que les mises à jour de l'interface utilisateur se produisent sur le bon thread, remplaçant DispatchQueue.main.async.
Async/Await est disponible dans Swift (iOS 13+), Kotlin (via kotlinx.coroutines), Dart (Flutter), JavaScript/TypeScript, Python, C#, Rust et Go (via goroutines avec sucre syntaxique).
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi