Qu'est-ce que le Streaming SSE et Pourquoi C'est Magique ?
Imaginez que vous envoyez un message à un assistant IA et que vous voyez les réponses apparaître mot par mot, comme si quelqu'un les tapait en temps réel devant vous. C'est exactement ce que permet la technologie Server-Sent Events (SSE) !
Dans ce tutoriel, nous allons apprendre ensemble à recevoir des flux de données en continu depuis une API IA. Vous n'avez besoin d'aucune expérience préalable en programmation d'API — nous partirons de zéro.
Prérequis Avant de Commencer
- Un navigateur web moderne (Chrome, Firefox, Edge)
- Un éditeur de texte (Visual Studio Code, Sublime Text, ou même le Bloc-notes)
- Un compte sur une plateforme IA — je vous recommande de vous inscrire ici sur HolySheep AI qui offre des crédits gratuits et une latence inférieure à 50ms
Comprendre le Principe du Streaming
Traditionnellement, quand vous demandez quelque chose à une API, vous attendez que le serveur calcule TOUTE la réponse avant de vous la renvoyer. Avec le streaming SSE, le serveur envoie les morceaux de réponse au fur et à mesure — comme un train qui arrive wagon par wagon plutôt que d'attendre que le convoi entier soit prêt.
Implémentation Pas à Pas
Étape 1 : Créer le Fichier HTML de Base
Créez un fichier nommé streaming-demo.html et ajoutez ce code :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Démonstration Streaming SSE</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
#response {
border: 2px solid #4CAF50;
border-radius: 10px;
padding: 20px;
min-height: 200px;
background-color: white;
margin-top: 20px;
}
#status {
color: #666;
font-style: italic;
}
</style>
</head>
<body>
<h1>Mon Premier Chat IA avec Streaming</h1>
<textarea id="message" rows="4" cols="60" placeholder="Tapez votre message ici..."></textarea>
<br><br>
<button id="sendBtn">Envoyer</button>
<p id="status">En attente...</p>
<div id="response"></div>
<script>
// Notre code JavaScript viendra ici
</script>
</body>
</html>
💡 Conseil : Vous devriez voir une zone de texte, un bouton vert, et un espace blanc où apparaîtront les réponses.
Étape 2 : Comprendre le Code JavaScript de Connexion
Ajoutez ce code JavaScript à l'intérieur des balises <script> de votre fichier :
const messageInput = document.getElementById('message');
const responseDiv = document.getElementById('response');
const statusText = document.getElementById('status');
const sendButton = document.getElementById('sendBtn');
// Remplacez par votre vraie clé API
const API_KEY = 'YOUR_HOLYSHEEP_API_KEY';
const BASE_URL = 'https://api.holysheep.ai/v1';
let eventSource = null;
function connectToStream(userMessage) {
// On vide la réponse précédente
responseDiv.innerHTML = '';
statusText.textContent = 'Connexion en cours...';
sendButton.disabled = true;
// Construire l'URL avec les paramètres
const url = ${BASE_URL}/chat/completions;
// Créer l'événement Source (la connexion SSE)
eventSource = new EventSourcePolyfill(url, {
headers: {
'Authorization': Bearer ${API_KEY},
'Content-Type': 'application/json'
},
query: {
'model': 'gpt-4.1',
'stream': 'true'
}
});
// Gérer les données reçues
eventSource.onmessage = function(event) {
statusText.textContent = 'Réception des données...';
try {
const data = JSON.parse(event.data);
// Vérifier si c'est la fin du flux
if (data.choices && data.choices[0].finish_reason === 'stop') {
statusText.textContent = 'Terminé !';
closeConnection();
return;
}
// Extraire et afficher le contenu
const content = data.choices[0].delta.content;
if (content) {
responseDiv.innerHTML += content;
}
} catch (e) {
console.log('Données partielles:', event.data);
}
};
// Gérer les erreurs de connexion
eventSource.onerror = function(error) {
console.error('Erreur de connexion:', error);
statusText.textContent = 'Erreur de connexion';
closeConnection();
// Implémenter la reconnexion automatique
setTimeout(() => {
statusText.textContent = 'Tentative de reconnexion...';
connectToStream(userMessage);
}, 3000);
};
}
function closeConnection() {
if (eventSource) {
eventSource.close();
eventSource = null;
}
sendButton.disabled = false;
}
// Gestion du clic sur le bouton
sendButton.addEventListener('click', function() {
const message = messageInput.value.trim();
if (message) {
connectToStream(message);
}
});
⚠️ Note importante : Cette méthode EventSource standard a des limitations avec les requêtes POST. Pour une implémentation complète, utilisez plutôt l'approche Fetch avec ReadableStream présentée ci-dessous.
Étape 3 : L'Approche Moderne avec Fetch et ReadableStream
Cette méthode est plus fiable et fonctionne parfaitement avec les API modernes comme HolySheep AI :
async function envoyerMessageStreaming(message) {
responseDiv.innerHTML = '';
statusText.textContent = 'Connexion en cours...';
sendButton.disabled = true;
try {
const response = await fetch(${BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${API_KEY},
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'gpt-4.1',
messages: [
{ role: 'user', content: message }
],
stream: true
})
});
if (!response.ok) {
throw new Error(Erreur HTTP: ${response.status});
}
// Obtenir le reader du flux
const reader = response.body.getReader();
const decoder = new TextDecoder();
statusText.textContent = 'Réception des données...';
// Lire le flux morceau par morceau
while (true) {
const { done, value } = await reader.read();
if (done) {
statusText.textContent = 'Terminé !';
sendButton.disabled = false;
break;
}
// Décoder les données binaires
const chunk = decoder.decode(value, { stream: true });
// Traiter chaque ligne SSE
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
// Ignorer le message de fin
if (data === '[DONE]') continue;
try {
const parsed = JSON.parse(data);
const content = parsed.choices?.[0]?.delta?.content;
if (content) {
responseDiv.innerHTML += content;
}
} catch (e) {
// Ignorer les erreurs de parsing partiel
}
}
}
}
} catch (error) {
console.error('Erreur:', error);
statusText.textContent = Erreur: ${error.message};
sendButton.disabled = false;
// Tentative de reconnexion après 5 secondes
setTimeout(() => {
if (confirm('Voulez-vous réessayer ?')) {
envoyerMessageStreaming(message);
}
}, 5000);
}
}
Comprendre la Reconnexion Automatique
Imaginez que vous regardez une vidéo et que votre WiFi coupe pendant 2 secondes. La vidéo ne s'arrête pas définitivement — elle reprend là où elle en était. C'est exactement le même principe pour notre connexion SSE !
Stratégie de Reconnexion Complète
class StreamingManager {
constructor() {
this.maxRetries = 5;
this.currentRetry = 0;
this.retryDelay = 1000;
this.isConnected = false;
this.abortController = null;
}
async envoyerAvecReconnexion(message) {
this.currentRetry = 0;
this.retryDelay = 1000;
while (this.currentRetry < this.maxRetries) {
try {
await this.faireRequeteStreaming(message);
this.currentRetry = 0; // Réussite, réinitialiser
return;
} catch (error) {
this.currentRetry++;
console.log(Tentative ${this.currentRetry}/${this.maxRetries});
if (this.currentRetry >= this.maxRetries) {
statusText.textContent = 'Nombre maximum de tentatives atteint';
alert('Impossible de se connecter. Veuillez vérifier votre connexion internet.');
return;
}
// Afficher le compte à rebours
await this.afficherCompteARebours();
// Augmenter le délai exponentiellement (1s, 2s, 4s, 8s...)
this.retryDelay = Math.min(this.retryDelay * 2, 30000);
}
}
}
async faireRequeteStreaming(message) {
// Annuler toute requête précédente
if (this.abortController) {
this.abortController.abort();
}
this.abortController = new AbortController();
const response = await fetch(${BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${API_KEY},
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'gpt-4.1',
messages: [{ role: 'user', content: message }],
stream: true
}),
signal: this.abortController.signal
});
if (!response.ok) {
throw new Error(Erreur HTTP: ${response.status});
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') continue;
try {
const parsed = JSON.parse(data);
const content = parsed.choices?.[0]?.delta?.content;
if (content) {
responseDiv.innerHTML += content;
}
} catch (e) {}
}
}
}
}
async afficherCompteARebours() {
const seconds = Math.ceil(this.retryDelay / 1000);
statusText.textContent = Reconnexion dans ${seconds} secondes...;
for (let i = seconds; i > 0; i--) {
statusText.textContent = Reconnexion dans ${i} secondes...;
await new Promise(r => setTimeout(r, 1000));
}
}
}
// Utilisation
const manager = new StreamingManager();
sendButton.addEventListener('click', () => {
const message = messageInput.value.trim();
if (message) {
manager.envoyerAvecReconnexion(message);
}
});
Les Avantages HolySheep AI Expliqués Simplement
- Taux de change avantageux : ¥1 = $1 — une économie de plus de 85% par rapport aux autres plateformes américaines
- Paiement flexible : Accepte WeChat Pay et Alipay, très pratique pour les utilisateurs chinois
- Réactivité exceptionnelle : Latence inférieure à 50ms, les réponses apparaissent quasi-instantanément
- Crédits gratuits : De nouveaux crédits offerts à l'inscription pour tester toutes les fonctionnalités
Comparaison des Prix 2026 (par million de tokens)
| Modèle | Prix Input | Prix Output |
|---|---|---|
| DeepSeek V3.2 | $0.42 | $0.42 |
| Gemini 2.5 Flash | $2.50 | $10 |
| GPT-4.1 | $8 | $24 |
| Claude Sonnet 4.5 | $15 | $75 |
Erreurs Courantes et Solutions
1. Erreur : "Failed to load - No 'Access-Control-Allow-Origin' header"
Cause : Votre page web et l'API ne sont pas sur le même domaine, et le navigateur bloque la requête pour des raisons de sécurité.
Solution : Vérifiez que votre clé API est valide et que vous avez correctement configuré les en-têtes CORS. Avec HolySheep AI, les en-têtes CORS sont déjà configurés — si cette erreur persiste, regeneratez votre clé API dans votre tableau de bord.
2. Erreur : "JSON.parse error on streaming data"
Cause : Vous recevez des données partielles qui ne sont pas du JSON valide.
Solution : Entourez votre parsing JSON d'un bloc try/catch et ignorez les erreurs partielles. Les données SSE arrivent souvent en plusieurs morceaux incomplets. Voici le code corrigé :
// Fragment de code pour gérer les données partielles
let buffer = ''; // Tampon pour accumulate
const chunk = decoder.decode(value, { stream: true });
buffer += chunk;
// Traiter les lignes complètes uniquement
const lines = buffer.split('\n');
buffer = lines.pop() || ''; // Garder le dernier fragment incomplet
for (const line of lines) {
if (line.startsWith('data: ') && line.length > 6) {
try {
const parsed = JSON.parse(line.slice(6));
// Traitement...
} catch (e) {
// Ignorer, c'est un fragment incomplet
}
}
}
3. Erreur : "Connection closed unexpectedly"
Cause : La connexion a été fermée brutalement par le serveur ou à cause d'un problème réseau.
Solution : Implémentez la reconnexion automatique avec backoff