Quand utiliser async vs sync
Mis à jour le 30 juillet 2026
Le choix n’est pas toujours évident
L’asynchrone n’est pas systématiquement supérieur au synchrone. Chaque approche a ses contextes où elle excelle, et choisir la mauvaise peut compliquer votre code sans gain de performance. Cette leçon vous aide à déterminer quelle approche adopter selon votre situation.
Quand le synchrone suffit
Le client synchrone reste le bon choix plus souvent qu’on ne le croit, et la raison est toujours la même : quand le parallélisme n’apporte rien, sa complexité est un coût net. Un script ponctuel de test ou d’exploration qui envoie quelques requêtes n’a rien à gagner à l’asynchrone — la simplicité du code prime, et vous relirez ce script dans six mois. Un pipeline séquentiel où chaque requête dépend du résultat de la précédente ne peut de toute façon rien paralléliser : l’asynchrone y ajouterait de la syntaxe sans changer une seconde au temps d’exécution. Même logique pour une application Django traditionnelle : si votre stack n’est pas asynchrone, greffer asyncio pour les seuls appels API crée une frontière sync/async pénible à maintenir. Et en phase de prototypage, quand vous testez un prompt ou validez un concept, le code synchrone se lit et s’écrit plus vite — c’est exactement ce qu’on demande à un prototype.
# Synchrone — parfait pour un pipeline séquentiel
from openai import OpenAI
client = OpenAI(api_key=os.getenv("XAI_API_KEY"), base_url="https://api.x.ai/v1")
# Étape 1 : analyser le texte
analysis = client.chat.completions.create(
model="grok-4.5",
messages=[{"role": "user", "content": f"Analyse ce texte : {text}"}]
).choices[0].message.content
# Étape 2 : générer un resume base sur l'analyse
summary = client.chat.completions.create(
model="grok-4.5",
messages=[{"role": "user", "content": f"Resume selon cette analyse : {analysis}"}]
).choices[0].message.content
Dans cet exemple, l’étape 2 a besoin du résultat de l’étape 1. L’asynchrone ne gagnerait rien.
Quand l’asynchrone est nécessaire
L’asynchrone devient incontournable dès que l’attente réseau domine votre temps d’exécution et que les requêtes sont indépendantes. Le cas d’école est le traitement par lots : des dizaines ou centaines de requêtes sans dépendance entre elles, où le gain est proportionnel au volume — cent requêtes de deux secondes prennent trois minutes en séquence, une dizaine de secondes en parallèle contrôlé. Le second cas n’est pas un choix mais une obligation : dans une application FastAPI ou Starlette, le framework est déjà asynchrone, et y utiliser un client synchrone bloquerait l’event loop — chaque appel à Grok gèlerait toutes les autres requêtes de votre serveur. Restent les architectures naturellement concurrentes : un chatbot qui interroge Grok, une base de données et une API externe en parallèle pour composer sa réponse, ou un système de monitoring qui surveille plusieurs flux à la fois — l’asynchrone y est simplement la forme naturelle du problème.
# Asynchrone — traitement de 50 questions en parallèle
async def batch_process(questions: list[str]) -> list[str]:
sem = asyncio.Semaphore(5)
async def ask(q: str) -> str:
async with sem:
resp = await client.chat.completions.create(
model="grok-4.5",
messages=[{"role": "user", "content": q}]
)
return resp.choices[0].message.content
return await asyncio.gather(*[ask(q) for q in questions])
Avec 50 questions et un sémaphore de 5, vous traitez 5 requêtes en parallèle au lieu d’une seule. Si chaque requête prend 8 secondes, le temps total passe de 400 secondes à environ 80 secondes.
Le piège de l’asynchrone premature
Un code asynchrone mal conçu est pire qu’un code synchrone bien écrit. Voici les erreurs courantes :
Bloquer l’event loop
# MAUVAIS : appel synchrone dans une fonction async
async def bad_example():
response = requests.get("https://api.example.com") # bloque tout
return response.json()
La bibliothèque requests est synchrone. L’appeler dans une coroutine bloque l’event loop et annulé tout bénéfice de l’asynchrone. Utilisez httpx ou aiohttp à la place.
Complexité inutile
# MAUVAIS : async pour une seule requête
async def over_engineered():
response = await client.chat.completions.create(...)
return response
# Équivalent synchrone, plus simple
def simple():
response = client.chat.completions.create(...)
return response
Si vous n’avez qu’une seule requête à envoyer, l’asynchrone ajoute de la complexité sans bénéfice.
Arbre de décision
Pour choisir entre sync et async, posez-vous ces questions dans l’ordre :
- Combien de requêtes API envoyez-vous ? Si une seule ou quelques-unes en séquence, restez en synchrone.
- Les requêtes sont-elles indépendantes ? Si chaque requête dépend de la précédente, restez en synchrone.
- Votre framework est-il asynchrone ? Si oui (FastAPI, Starlette, aiohttp), utilisez l’asynchrone pour ne pas bloquer l’event loop.
- Le temps de traitement total est-il un problème ? Si vous traitez des lots et que le temps synchrone est inacceptable, passez en asynchrone.
Points clés à retenir
- Le synchrone convient aux scripts simples, pipelines séquentiels et prototypage
- L’asynchrone est nécessaire pour le traitement par lots, les frameworks async et les requêtes parallèles
- Ne jamais utiliser de bibliothèques synchrones (
requests) dans du code asynchrone - La complexité ajoutée par l’asynchrone doit être justifiée par un gain mesurable
- Commencez en synchrone, migrez en asynchrone quand le besoin se confirme