使用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
相关产品推荐
相关产品推荐

