SSE入门:如何实现仅在数据变化时推送数据
如何让SSE流仅在底层数据变化时发布数据
你刚接触SSE,目前通过while(true)循环定时推送数据,希望优化成只有底层数据发生变化时才发布更新,初始实现代码如下:
const express = require('express'); run().catch(err => console.log(err)); async function run() { const app = express(); app.get('/events', async function (req, res) { console.log('Got /events'); res.set({ 'Cache-Control': 'no-cache', 'Content-Type': 'text/event-stream', 'Connection': 'keep-alive' }); res.flushHeaders(); // 告诉客户端连接断开后每10秒重试一次 res.write('retry: 10000\n\n'); let count = 0; while (true) { await new Promise(resolve => setTimeout(resolve, 1000)); console.log('推送数据', ++count); // 推送包含当前count值的SSE res.write(`data: ${count}\n\n`); } }); await app.listen(3000); console.log('监听端口3000');
你已实现的按需推送逻辑
你通过维护全局连接数变量,实现了仅在新客户端连接时推送更新的功能,修改后的代码如下:
const express = require('express'); run().catch(err => console.log(err)); async function run() { const app = express(); let connections = 0 app.get('/events', async function (req, res) { console.log('收到/events请求'); res.set({ 'Cache-Control': 'no-cache', 'Content-Type': 'text/event-stream', 'Connection': 'keep-alive' }); res.flushHeaders(); connections += 1 let localConnections = connections res.write(`data: ${connections}\n\n`); while (true) { await new Promise(resolve => setTimeout(resolve, 1000)); if (localConnections !== connections) { localConnections = connections console.log('推送数据', connections); // 推送包含当前连接数的SSE res.write(`data: ${connections}\n\n`); } } }); await app.listen(3000); console.log('监听端口3000'); }
这个逻辑的核心是:用全局变量connections记录当前连接数,每个新连接建立时更新该变量;在循环中对比本地缓存的localConnections和全局值,仅当两者不一致(即有新连接)时才推送数据,避免了无意义的定时推送。
更通用的事件驱动实现思路
如果要适配其他数据变化场景(比如数据库更新、文件修改、外部API回调等),可以用事件驱动的方式完全替代轮询循环,进一步优化性能:
- 创建一个事件触发器,监听底层数据的变化
- 每个SSE连接订阅该触发器,数据变化时直接推送更新
- 客户端断开连接时取消订阅,避免内存泄漏
示例代码:
const express = require('express'); const EventEmitter = require('events'); run().catch(err => console.log(err)); async function run() { const app = express(); // 创建事件触发器,用于广播数据变化 const dataChangeEmitter = new EventEmitter(); let connections = 0; // 模拟真实场景中的数据变化(实际可替换为数据库监听、文件监听等逻辑) setInterval(() => { connections += 1; // 触发数据变更事件,传递最新数据 dataChangeEmitter.emit('dataUpdated', connections); }, 5000); app.get('/events', async function (req, res) { console.log('收到/events请求'); res.set({ 'Cache-Control': 'no-cache', 'Content-Type': 'text/event-stream', 'Connection': 'keep-alive' }); res.flushHeaders(); // 首次连接时推送当前最新数据 res.write(`data: ${connections}\n\n`); // 订阅数据变更事件,发生变化时立即推送 const handleDataUpdate = (newData) => { res.write(`data: ${newData}\n\n`); }; dataChangeEmitter.on('dataUpdated', handleDataUpdate); // 客户端断开连接时取消订阅,清理资源 req.on('close', () => { dataChangeEmitter.off('dataUpdated', handleDataUpdate); }); }); await app.listen(3000); console.log('监听端口3000'); }
这种方式不需要持续轮询,只有数据真的发生变化时才会推送更新,既节省服务器资源,又能降低数据推送的延迟。
内容的提问来源于stack exchange,提问作者BlazinBlazor
相关产品推荐
相关产品推荐

