You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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回调等),可以用事件驱动的方式完全替代轮询循环,进一步优化性能:

  1. 创建一个事件触发器,监听底层数据的变化
  2. 每个SSE连接订阅该触发器,数据变化时直接推送更新
  3. 客户端断开连接时取消订阅,避免内存泄漏

示例代码:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 15:50:31