1
0
forked from erp-dev/erp
Files
erpnew/sse/client_example.js

187 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* SSE客户端示例
*
* 由于浏览器原生的EventSource不支持自定义请求头
* 这里提供了两种实现方式:
* 1. 使用fetch实现推荐
* 2. 使用EventSource polyfill备选
*/
/**
* 方式1使用fetch实现SSE客户端推荐
*
* @param {string} url - SSE端点URL
* @param {string} token - JWT认证token
* @param {function} onMessage - 接收到消息时的回调函数
* @param {function} onError - 连接错误时的回调函数
* @param {function} onClose - 连接关闭时的回调函数
*/
function createSSEConnectionWithFetch(url, token, onMessage, onError, onClose) {
const controller = new AbortController();
const signal = controller.signal;
// 启动一个长时间运行的fetch请求
fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'text/event-stream',
'Cache-Control': 'no-cache',
},
signal: signal
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
function processBuffer() {
const lines = buffer.split('\n');
buffer = lines.pop(); // 保留最后一个不完整的行
for (const line of lines) {
if (line.trim() === '') continue; // 空行表示事件结束
if (line.startsWith('data: ')) {
const data = line.substring(6); // 去掉 'data: ' 前缀
try {
const event = JSON.parse(data);
if (onMessage) onMessage(event);
} catch (e) {
console.error('Error parsing SSE data:', e);
}
}
}
}
function read() {
return reader.read().then(({ done, value }) => {
if (done) {
if (onClose) onClose();
return;
}
buffer += decoder.decode(value, { stream: true });
processBuffer();
// 继续读取
return read();
});
}
return read();
})
.catch(error => {
if (onError) onError(error);
});
// 返回一个对象,包含关闭连接的方法
return {
close: () => controller.abort()
};
}
/**
* 方式2使用EventSource Polyfill实现
*
* 需要先安装EventSource polyfill
* npm install event-source-polyfill
*
* 在应用入口处导入:
* import 'event-source-polyfill';
*/
function createSSEConnectionWithPolyfill(url, token, onMessage, onError, onClose) {
// 创建带有认证的URL
const urlWithAuth = `${url}?token=${encodeURIComponent(token)}`;
const eventSource = new EventSource(urlWithAuth);
eventSource.onmessage = function(event) {
try {
const data = JSON.parse(event.data);
if (onMessage) onMessage(data);
} catch (e) {
console.error('Error parsing SSE data:', e);
}
};
eventSource.onerror = function(error) {
if (onError) onError(error);
};
eventSource.onclose = function() {
if (onClose) onClose();
};
return {
close: () => eventSource.close()
};
}
/**
* 使用示例
*/
const JWT_TOKEN = 'your_jwt_token_here';
const SSE_URL = '/sse/';
// 使用fetch方式推荐
const sseConnection = createSSEConnectionWithFetch(
SSE_URL,
JWT_TOKEN,
(event) => {
console.log('收到SSE事件:', event);
// 根据事件类型处理不同业务逻辑
switch (event.type) {
case 'connected':
console.log(`SSE连接成功商户ID: ${event.merchant_id}`);
break;
case 'order_paid':
console.log(`订单已支付: ${event.object_id}`);
// 刷新订单列表或显示通知
break;
case 'stock_change_record':
console.log(`库存变动记录: ${event.object_id}`);
// 刷新库存数据
break;
case 'server_shutdown':
console.log('服务器即将关闭连接');
// 可以提示用户重新连接
break;
}
},
(error) => {
console.error('SSE连接错误:', error);
// 可以在这里实现重连逻辑
setTimeout(() => {
console.log('尝试重新连接...');
// 重新创建连接
}, 5000);
},
() => {
console.log('SSE连接已关闭');
}
);
// 当需要关闭连接时(例如用户退出登录)
// sseConnection.close();
/**
* 如果需要使用polyfill方式确保在应用入口处导入polyfill
*/
// import 'event-source-polyfill';
//
// const sseConnection = createSSEConnectionWithPolyfill(
// SSE_URL,
// JWT_TOKEN,
// (event) => { /* 处理事件 */ },
// (error) => { /* 处理错误 */ },
// () => { /* 处理关闭 */ }
// );