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

DataTables对接Ajax和WebSocket双数据源实现表格列实时更新问题

问题分析与优化实现

原代码无法运行的核心原因

  • 表格行匹配逻辑错误:rows({ search: data.s })是全局模糊搜索,会匹配所有列内容,无法精准匹配symbol+pair的组合,而且每次遍历全表性能极差
  • 初始化时序问题:WebSocket初始化在AJAX请求外部,可能AJAX还没完成表格初始化,WebSocket推送就已触发,调用DataTable()会抛出未初始化错误
  • 数据更新后未重绘:修改cell数据后没有调用draw()方法,页面不会渲染最新值
  • 重复获取实例性能损耗:每次WebSocket推送都重新获取DataTable实例,产生不必要的性能开销

优化实现方案

核心优化点是提前建立交易对-行索引的映射表,WebSocket推送时直接O(1)查找对应行,不需要每次遍历全表,性能提升非常明显。

完整修改后代码

首先定义全局变量存储表格实例和映射表:

// 全局变量,存储表格实例和交易对到行索引的映射
let tenantsTable = null;
let pairRowMap = new Map();

然后修改AJAX回调部分:

abp.ajax({
    method: 'POST',
    url: abp.appPath + 'Portfolio/GetSignalsByPeriod/',
    data: JSON.stringify(PeriodId),
    dataType: 'json',
    contentType: 'application/json',
}).done(function (result) {
    // 初始化映射表:拼接symbol+pair作为key,行索引作为value
    result.forEach((row, index) => {
        const fullPair = `${row.symbol}${row.pair}`.toUpperCase(); // 统一大写避免匹配问题
        pairRowMap.set(fullPair, index);
    });

    tenantsTable = $('#TenantsTable').DataTable({
        searching: true,
        ordering: true,
        data: result,
        columns: [
            { data: 'symbol' },
            { data: 'pair' },
            { data: 'signal' },
            {
                data: 'signalDate',
                render: function (data) {
                    return moment(new Date(data)).format('DD/MM/YYYY - h:mm:ss');
                }
            },
            { data: 'signalPrice' },
            { 
                data: 'lastPrice', // 绑定字段方便后续更新
                defaultContent: '-'
            },
            { 
                data: 'change', 
                defaultContent: '-'
            },
            { 
                data: 'sltp', 
                defaultContent: '-'
            }
        ],
        buttons: [
            {
                name: 'refresh-usdt',
                text: 'USDT',
                action: function (e, dt) {
                    dt.column(1).search("USDT").draw();
                }
            },
            {
                name: 'refresh-btc',
                text: 'BTC',
                action: function (e, dt) {
                    dt.column(1).search("BTC").draw();
                }
            },
        ]
    });

    // 表格初始化完成后再初始化WebSocket,避免时序问题
    initWebSocket();
})

最后是WebSocket部分的实现:

function initWebSocket() {
    const url = 'wss://stream.binance.com:9443/ws/!ticker@arr';
    const ws = new WebSocket(url);
    ws.onmessage = (event) => {
        const dataWs = JSON.parse(event.data);
        // 批量更新所有匹配行,最后统一重绘,减少重绘次数提升性能
        dataWs.forEach(ticker => {
            const fullPair = ticker.s.toUpperCase();
            // 映射表中存在对应交易对才处理
            if (pairRowMap.has(fullPair)) {
                const rowIndex = pairRowMap.get(fullPair);
                const rowData = tenantsTable.row(rowIndex).data();
                // 更新三个字段的值,Binance ticker字段:c为最新成交价、P为涨跌幅百分比
                rowData.lastPrice = ticker.c;
                rowData.change = `${ticker.P}%`;
                // 止盈止损可根据你的业务逻辑计算,以下为示例代码可自行调整
                rowData.sltp = `SL: ${(rowData.signalPrice * 0.95).toFixed(2)} / TP: ${(rowData.signalPrice * 1.05).toFixed(2)}`;
                // 更新行数据
                tenantsTable.row(rowIndex).data(rowData);
            }
        });
        // 所有更新完成后统一重绘,false参数表示保持当前分页状态
        tenantsTable.draw(false);
    };
}

额外优化说明

  • 映射表的方式可以把每次推送的匹配耗时从O(n)降到O(1),即使表格有上千行也不会卡顿
  • 批量更新后统一重绘,避免每次修改单元格就重绘一次,大幅减少页面重排重绘开销
  • 统一交易对大小写,避免Binance返回的大写和你表格里的小写匹配失败的问题
  • WebSocket初始化放在表格初始化完成后,彻底解决时序问题

内容的提问来源于stack exchange,提问作者Murat Şanlısavaş

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 08:15:04