Que se cache-t-il derrière le mot-clé suspend en Kotlin ?
\n\nSi vous avez déjà écrit en Kotlin et essayé d'appeler une fonction marquée du modificateur suspend, mais sans coroutine — vous avez très probablement obtenu une erreur de compilation. Et ce n'est pas un bug, c'est une fonctionnalité. Les fonctions suspend sont la base de la programmation asynchrone en Kotlin. En 2025, elles sont devenues encore plus pratiques, mais les questions des débutants (et même des développeurs expérimentés) n'ont pas disparu. L'une des plus populaires sur Stack Overflow — « What does a suspend function mean in Kotlin coroutines? » — a accumulé des milliers de vues. Comprenons cela une fois pour toutes.
Une fonction suspend — pas de la magie, mais un contrat
\n\nImaginez que vous commandez une pizza. Vous appelez le restaurant, passez commande, et au lieu d'attendre au téléphone 20 minutes, vous continuez vos occupations. Quand la pizza est prête, on vous rappelle — vous « revenez » à la conversation. C'est exactement ce qu'est une fonction suspend : elle peut suspendre son exécution sans bloquer le thread, puis reprendre lorsque le résultat est disponible.
\n\nTechniquement, suspend n'est pas un mot-clé qui rend une fonction asynchrone « de nulle part ». C'est un contrat : la fonction peut se suspendre, mais n'y est pas obligée. Le compilateur Kotlin transforme une telle fonction en machine à états qui sait sauvegarder et restaurer son état.
À quoi ressemble une fonction suspend dans le code ?
\n\nVoici un exemple très simple :
\n\nsuspend fun fetchUserData(userId: String): User {\n delay(1000) // simulation d'une requête réseau\n return User(userId, "Alexei")\n}\n\nVous ne pouvez pas appeler fetchUserData simplement, en dehors d'une coroutine :
// Erreur de compilation !\nfun main() {\n val user = fetchUserData("123") // Suspend function 'fetchUserData' should be called only from a coroutine or another suspend function\n}\n\nPour l'appeler, vous devez être à l'intérieur d'une coroutine. Par exemple :
\n\nfun main() = runBlocking {\n val user = fetchUserData("123")\n println(user.name) // Alexei\n}\n\nrunBlocking est un pont entre le monde normal et le monde des coroutines. Dans les projets réels, vous ne l'utiliserez presque jamais en production (sauf pour les tests et le main). À la place, vous lancerez des coroutines via CoroutineScope.
Que se passe-t-il « sous le capot » ?
\n\nLorsque le compilateur voit suspend, il génère un paramètre supplémentaire — un objet Continuation. Cet objet stocke le point où la coroutine a été suspendue, ainsi que toutes les variables locales. Grâce à cela, la coroutine peut « s'endormir » dans un thread et « se réveiller » dans un autre — sans blocage.
Important : une fonction suspend ne bloque pas le thread. Pendant qu'elle « dort » (par exemple, en attendant une réponse du serveur), le thread peut exécuter d'autres tâches. C'est la différence clé avec les anciennes approches (Thread.sleep, callback hell).
\n\nMeilleures pratiques pour l'utilisation des fonctions suspend en 2025
\n\nCes dernières années, l'écosystème Kotlin a beaucoup grandi. De nouvelles bibliothèques sont apparues, le compilateur et l'IDE se sont améliorés. Voici ce qu'il faut savoir et appliquer dès maintenant.
\n\n1. Ne créez pas de fonctions suspend qui ne se suspendent pas
\n\nSi une fonction ne contient pas d'appels à d'autres fonctions suspend (par exemple, delay, withContext, appel API via Ktor/Retrofit), ne lui mettez pas suspend. Cela induit en erreur et ajoute une surcharge inutile.
// Mauvais\nsuspend fun formatName(first: String, last: String): String {\n return "$first $last".trim()\n}\n\n// Bon\nfun formatName(first: String, last: String): String {\n return "$first $last".trim()\n}\n\n2. Utilisez withContext pour changer de dispatcher
\n\nSi vous devez effectuer une opération bloquante (lecture de fichier, travail avec une base de données), ne marquez pas la fonction comme suspend et ne la rendez pas