Chart.js 3.8.0 折线图定时更新数据后无法显示问题排查
代码错误点与修复方案
你的代码存在4个核心问题,按影响优先级排序:
- 字段名不符合Chart.js规范:Chart.js 接收的数据集配置字段为复数形式
datasets,你写的是单数dataset,图表初始化时根本读不到数据系列配置,这是最核心的不显示原因。同时标签数组labels是挂载在data配置下,不是config根属性,你之前传参给setLabels时路径写错误读了undefined。 - 异步请求时序错误:你封装的
getPriceData__是回调风格的XHR请求,没有返回Promise对象,外层加await完全无法等待请求返回。实际执行时定时器里调用getPriceData()后会立刻执行后续的pop、更新图表逻辑,此时接口数据还没存入chartCryptoData数组,大部分时候取到的都是空值。 - 数组裁剪逻辑错误:你初始化的
labels和价格数据数组都是空数组,每次push新值之后立刻调用shift()删除数组首项——空数组push后长度为1,shift后长度直接回到0,数组永远存不下可渲染的数据点,相当于加了数据又立刻删掉。 - PHP输出未做转义:直接把Session变量输出到JS字符串中,如果变量包含引号、特殊字符会直接触发JS语法错误,应该用
json_encode做安全转义。
修复后核心代码
<div> <canvas class="realTimeGraph" id="realTimeGraph"></canvas> </div> <script type="text/javascript"> var ctx = document.getElementById("realTimeGraph").getContext('2d'); const BaseUrl = "http://localhost:3000/CryptoSite/php/dbEndpoint.php?crypto=" // 配置图表最多展示的点位数量,初始化时预填空值避免shift清空全部数据 const maxDataPoint = 10; let chartCryptoData = []; let data = { labels: Array(maxDataPoint).fill(''), // 修正字段名为datasets(复数) datasets: [{ label: "Crypto price", data: Array(maxDataPoint).fill(null), backgroundColor: 'rgb(255, 99, 132)', borderColor: 'rgb(255, 99, 132)', tension: 0.1 }] }; const config = { type: 'line', data : data, options : {} }; // 将XHR封装为Promise形式,让await可以正常等待请求完成 function getPriceData__(url) { return new Promise((resolve, reject) => { const xhttp = new XMLHttpRequest(); xhttp.open("GET", url, true); xhttp.responseType = 'json'; xhttp.send(); xhttp.onload = () => { var status = xhttp.status; if (status === 200) { resolve(xhttp.response); } else { reject(status); } }; xhttp.onerror = () => reject(xhttp.status); }) } async function getPriceData() { // 用json_encode转义PHP输出,避免特殊字符触发JS语法错误 let cryptoForGraph = <?php echo json_encode($_SESSION['cryptoForGraph'] ?? '');?>; let url = BaseUrl + cryptoForGraph try { const res = await getPriceData__(url); chartCryptoData.push({ x: res['timestamp'], y: res['price'] }); } catch (err) { console.log('Something went wrong: ' + err); } } function setLabels(labels, timestamp) { labels.push(timestamp); // 数组长度超过上限再删除旧数据,避免清空全部点位 if (labels.length > maxDataPoint) labels.shift(); } function setData(data, price) { data.push(price); if (data.length > maxDataPoint) data.shift(); } document.getElementById("showCryptoGraph").onclick = async () => { console.log("button press"); var myLineChart = new Chart(ctx, config); // 首次加载先拉取一次数据,无需等第一个5秒周期 await getPriceData(); setInterval( async () => { await getPriceData(); let currentData = chartCryptoData.pop(); if(currentData !== undefined) { // 修正labels、datasets的读取路径,从config.data下取值 setData(config.data.datasets[0].data, currentData.y); setLabels(config.data.labels, currentData.x); myLineChart.update(); console.log("end interval"); } }, 5000); }; </script>
修复逻辑说明:初始化时提前给labels和data数组填充固定长度的空值,只有当数组长度超过最大展示点位数量时才执行shift删除旧数据,保证图表始终有可渲染的连续数据;把XHR请求改成Promise形式后,await可以正确等待接口返回再执行后续更新逻辑,不会出现取空值的问题。
内容的提问来源于stack exchange,提问作者EvoKin17
相关产品推荐
相关产品推荐

