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

使用Server-Sent Events实现Razor页面实时股价遇自动刷新问题

问题排查与修复方案

1. 前端JavaScript代码错误(核心问题)

语法错误

事件监听代码末尾缺少闭合括号,导致JS执行中断:
原代码:

eventSource.addEventListener("stock-update", function (event) {
    const stockData = JSON.parse(event.data);
    updateTable(stockData);
};

修正后:

eventSource.addEventListener("stock-update", function (event) {
    const stockData = JSON.parse(event.data);
    updateTable(stockData);
});

DOM更新逻辑错误

  • 错误使用$(id)非JS语法,应通过stockData.Id获取股票代码
  • 直接赋值DOM元素无效,需修改textContent属性
  • 字段大小写不匹配:后端返回Bid/Ask/Last/Timestamp(首字母大写),前端误用小写

原updateTable函数:

function updateTable(stockData) {
    document.getElementById('bid_$(id)') = stockData.bid;
    document.getElementById('ask_$(id)') = stockData.ask;
    document.getElementById('last_$(id)') = stockData.last;
    document.getElementById('timestamp_$(id)') = stockData.timestamp;
}

修正后:

function updateTable(stockData) {
    const stockId = stockData.Id;
    document.getElementById(`bid_${stockId}`).textContent = stockData.Bid;
    document.getElementById(`ask_${stockId}`).textContent = stockData.Ask;
    document.getElementById(`last_${stockId}`).textContent = stockData.Last;
    document.getElementById(`timestamp_${stockId}`).textContent = stockData.Timestamp;
}

2. 后台服务未注册导致无推送

Program.cs中仅注册了IServerSentEventsService,但StocksService作为后台定时服务未被添加到托管服务列表,导致定时更新逻辑完全未运行。

原Program.cs服务注册段:

builder.Services.AddSingleton<IServerSentEventsService, ServerSentEventsService>();

需新增后台服务注册:

builder.Services.AddHostedService<StocksService>();

完整服务注册修正后:

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddSingleton<Data>();
builder.Services.AddHttpClient();
builder.Services.AddServerSentEvents();
builder.Services.AddSingleton<IServerSentEventsService, ServerSentEventsService>();
builder.Services.AddHostedService<StocksService>(); // 新增该行

3. 随机数生成逻辑问题

StocksService中每次循环创建Random实例,会因系统时钟精度问题生成重复随机数,应将Random声明为类级静态成员:

原代码:

foreach (var (id, quotes) in Data.DataDict)
{
    Random rand = new Random();
    quotes.Bid += rand.Next(-1, 1);
    // ...
}

修正后:

private static readonly Random _rand = new Random(); // 类级静态成员

protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
    while(!cancellationToken.IsCancellationRequested)
    {
        IReadOnlyCollection<IServerSentEventsClient> clients = _service.GetClients();
        if (clients.Any())
        {
            foreach (var (id, quotes) in Data.DataDict)
            {
                // Next(-1,2)会生成-1、0、1三个值,原Next(-1,1)只会生成-1和0
                quotes.Bid += _rand.Next(-1, 2);
                quotes.Ask += _rand.Next(-1, 2);
                quotes.Last += _rand.Next(-1, 2);
                quotes.Timestamp = DateTime.Now.ToLongTimeString();
                // ...
            }
            await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
        }
    }
}

4. 额外优化:减少重复推送

当前逻辑会给每只股票单独推送SSE事件,5秒内推送5次,可优化为一次推送所有股票更新:

修改StocksService推送逻辑:

// 收集所有股票更新
var updates = Data.DataDict.Select(kv => new
{
    Id = kv.Key,
    Bid = kv.Value.Bid,
    Ask = kv.Value.Ask,
    Last = kv.Value.Last,
    Timestamp = kv.Value.Timestamp
}).ToList();

string json = JsonConvert.SerializeObject(updates);

ServerSentEvent data = new ServerSentEvent
{
    Id = "stock-update",
    Type = "stock-update",
    Data = new List<string>() { json },
};

await _service.SendEventAsync(data, cancellationToken);

对应前端JS修改为处理数组:

eventSource.addEventListener("stock-update", function (event) {
    const stockUpdates = JSON.parse(event.data);
    stockUpdates.forEach(update => updateTable(update));
});

内容的提问来源于stack exchange,提问作者Douar Gwenn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 17:04:56