Pourquoi votre await fetch() ralentit votre application — et comment y remédier
Lors du développement d'applications JavaScript, les problèmes de performance liés à fetch() sont souvent négligés. Pourtant, même un code simple comme await fetch() peut ralentir votre application de manière inattendue, provoquant des retards dans les requêtes réseau et frustrant les utilisateurs. Cet article explore les raisons de ces ralentissements et propose des solutions pour les résoudre.
1. Connexions TCP froides : des retards de 200ms sans raison apparente Les symptômes incluent une première requête à une API systématiquement plus lente que les suivantes. Chaque fetch() crée une nouvelle connexion socket, impliquant une résolution DNS, une poignée de main TCP et une négociation TLS. En Europe, le temps moyen d'un aller-retour (RTT) est d'environ 50ms, ce qui peut ajouter des centaines de millisecondes à chaque nouvelle requête.
La solution consiste à utiliser apiAgent pour gérer les connexions persistantes (keep-alive). La fonction fetchOrders() peut alors utiliser undici pour réutiliser efficacement les connexions ouvertes.
2. DNS + TLS : les dragons cachés Même avec keep-alive, la première requête vers un nouveau domaine reste lente en raison des recherches DNS et des négociations TLS. Une recherche DNS peut bloquer le thread JavaScript et prendre jusqu'à 100ms sur les réseaux mobiles.
Pour y remédier, il faut mettre en cache les requêtes DNS et augmenter maxSockets pour gérer plusieurs domaines. Une alternative consiste à utiliser QUIC/HTTP-3 avec 0-RTT pour contourner ces délais.
3. response.json() bloquant la boucle d'événements Lorsqu'un serveur envoie des réponses JSON volumineuses (5-10MB ou plus), l'appel à response.json() peut bloquer la boucle d'événements et consommer 100% du CPU.
La solution est d'utiliser un parsing en flux continu, qui traite le JSON au fur et à mesure de sa réception plutôt que de charger tout le contenu en mémoire d'un coup.
4. Optimisation de la taille des réponses : compression et formats Si la vitesse réseau est bonne mais que le chargement reste lent, le problème vient souvent de formats de données inefficaces ou d'un manque de compression.
Il faut activer la compression Brotli à la fois côté frontend et backend. Brotli permet d'économiser jusqu'à 25% d'espace supplémentaire par rapport à Gzip.
5. await dans une boucle : le tueur de concurrence Lorsque de nombreuses tâches asynchrones sont exécutées dans une boucle, elles le sont séquentiellement même si elles pourraient être concurrentes.
Pour éviter cela, il faut limiter la concurrence en s'assurant qu'un nombre limité de requêtes soient envoyées simultanément, évitant ainsi de surcharger le backend.
6. Utiliser undici.request pour des requêtes plus rapides Pour de meilleures performances, il est recommandé d'utiliser la fonction request de undici plutôt que le fetch natif, car elle est plus rapide et réduit la surcharge.
7. Simplification des pré-vérifications CORS Les requêtes de pré-vérification CORS ajoutent des délais supplémentaires. Simplifier les requêtes et mettre en cache les réponses de pré-vérification peut aider à réduire cette surcharge.
8. Blocage HOL dans HTTP/2 : les téléchargements de fichiers volumineux affectent toutes les requêtes Dans HTTP/2, les téléchargements de fichiers volumineux peuvent bloquer d'autres requêtes en raison du multiplexage sur une seule connexion TCP. Pour éviter cela, il faut séparer les requêtes volumineuses des petites.
Pour des performances encore meilleures, HTTP/3 fonctionne sur UDP et évite le goulot d'étranglement de TCP.
9. JSON.stringify() avant envoi La sérialisation de charges utiles volumineuses avec JSON.stringify() peut bloquer la boucle d'événements. Il faut plutôt utiliser des téléchargements multipart en flux continu ou d'autres méthodes de sérialisation efficaces.
Conclusion Optimiser les performances de await fetch() implique de résoudre plusieurs goulots d'étranglement. De la réutilisation des connexions au passage à des formats de streaming efficaces, chaque solution contribue à des requêtes réseau plus rapides et plus efficaces.