背景是AI对话中流式获取接口返回的数据,前端动态渲染返回的markdown 基础写法
class SSEClient {
constructor(url, options = {}) {
this.url = url;
this.options = options;
this.eventSource = null;
this.listeners = new Map();
}
connect() {
this.eventSource = new EventSource(this.url, this.options);
this.eventSource.onopen = (event) => {
console.log('SSE 连接已建立');
this.emit('open', event);
};
this.eventSource.onmessage = (event) => {
const data = this.parseData(event.data);
this.emit('message', data);
};
this.eventSource.onerror = (event) => {
console.error('SSE 连接错误:', event);
this.emit('error', event);
};
return this;
}
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
return this;
}
emit(event, data) {
const callbacks = this.listeners.get(event) || [];
callbacks.forEach(callback => callback(data));
}
parseData(data) {
try {
return JSON.parse(data);
} catch {
return { content: data };
}
}
close() {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
}
}
// 使用示例
const sseClient = new SSEClient('/api/events')
.on('open', () => console.log('连接成功'))
.on('message', (data) => {
if (data.type === 'chat') {
updateChatMessage(data.content);
}
})
.on('error', (error) => console.error('连接错误:', error))
.connect();
自动重连
class ReconnectingSSE {
constructor(url, options = {}) {
this.url = url;
this.options = {
maxRetries: 5,
retryDelay: 1000,
...options
};
this.retryCount = 0;
this.eventSource = null;
this.listeners = new Map();
}
connect() {
try {
this.eventSource = new EventSource(this.url);
this.eventSource.onopen = () => {
console.log('SSE 重连成功');
this.retryCount = 0;
this.emit('open');
};
this.eventSource.onmessage = (event) => {
this.emit('message', JSON.parse(event.data));
};
this.eventSource.onerror = () => {
this.eventSource.close();
this.reconnect();
};
} catch (error) {
this.reconnect();
}
}
reconnect() {
if (this.retryCount >= this.options.maxRetries) {
this.emit('maxRetriesReached');
return;
}
this.retryCount++;
const delay = this.options.retryDelay * Math.pow(2, this.retryCount - 1);
console.log(`${delay}ms 后重连 (${this.retryCount}/${this.options.maxRetries})`);
setTimeout(() => {
this.connect();
}, delay);
}
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
return this;
}
emit(event, data) {
const callbacks = this.listeners.get(event) || [];
callbacks.forEach(callback => callback(data));
}
}