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

如何在Express+EJS项目中从MySQL取数渲染Chart.js图表?

问题解决:Express后端异步数据传递到EJS渲染Chart.js

核心问题分析

你的代码核心问题是异步逻辑处理混乱:

  • getChartData标记了async但内部用回调式connection.query,未返回Promise,导致await getChartData()拿不到实际数据,最终得到undefined,传递给EJS的是空值。
  • 嵌套的connection.query直接同步调用,根本无法获取异步查询结果,后续代码会跳过等待直接执行。

修复步骤

1. 封装MySQL查询为Promise

把回调式的connection.query转为Promise,让async/await能正确处理异步流程:

// 工具函数:将connection.query封装为Promise
function query(sql, params) {
  return new Promise((resolve, reject) => {
    connection.query(sql, params, (error, results) => {
      if (error) reject(error);
      else resolve(results);
    });
  });
}

2. 修正getChartData的异步逻辑

用await替代回调,确保所有数据库查询完成后再处理数据:

async function getChartData() {
  const now = new Date();
  const formattedDate = now.toISOString().split('T')[0];

  try {
    // 等待sample表查询结果
    const sampleResults = await query("SELECT * FROM sample WHERE Date = ?", [formattedDate]);
    
    if (sampleResults.length === 0) {
      console.log("今日无数据,使用测试数据");
      return [
        { x: 50, y: 7 },
        { x: 60, y: 8 },
        { x: 70, y: 8 },
        { x: 80, y: 9 },
        { x: 90, y: 9 },
        { x: 100, y: 9 },
        { x: 110, y: 10 },
        { x: 120, y: 11 },
        { x: 130, y: 14 },
        { x: 140, y: 14 },
        { x: 150, y: 15 }
      ];
    }

    const AveragesList = [];
    const sampleHoles = [2, 12, 16];
    
    for (const holeid of sampleHoles) {
      // 依次等待每个嵌套查询完成
      const holeRecords = await query("SELECT * FROM holes WHERE Id = ?", [sampleResults[0][holeid]]);
      const holeRecord = holeRecords[0];
      
      const upSlopeRecords = await query("SELECT * FROM upslopespeed WHERE UpSlopeID = ?", [holeRecord.UpSlopeSpeedID]);
      const upSlopeData = upSlopeRecords[0];
      
      const downSlopeRecords = await query("SELECT * FROM downslopespeed WHERE DownSlopeID = ?", [holeRecord.DownSlopeSpeedID]);
      const downSlopeData = downSlopeRecords[0];

      // 计算平均速度
      const backLeftSpeed = (upSlopeData.BackLeft * 2) / (upSlopeData.BackLeft + downSlopeData.BackLeft);
      const backCentreSpeed = (upSlopeData.BackCentre * 2) / (upSlopeData.BackCentre + downSlopeData.BackCentre);
      const backRightSpeed = (upSlopeData.BackRight * 2) / (upSlopeData.BackRight + downSlopeData.BackRight);

      const AverageSpeed = (backLeftSpeed + backCentreSpeed + backRightSpeed) / 3;
      AveragesList.push(AverageSpeed);
    }

    return [
      {x:2, y:AveragesList[0]},
      {x:12, y:AveragesList[1]},
      {x:16, y:AveragesList[2]}
    ];
  } catch (error) {
    console.error("数据库查询错误:", error);
    // 出错时返回兜底数据
    return [
      { x: 50, y: 7 },
      { x: 60, y: 8 },
      { x: 70, y: 8 }
    ];
  }
}

3. 修正路由函数的异步处理

在路由中用await正确获取数据,直接传递对象给EJS,避免提前JSON.stringify:

async function DashboardDetermine(req, res) {
  if (req.session.user.isAdmin === 1) {
    try {
      // 等待用户列表查询完成
      const userResults = await query("SELECT * FROM user WHERE admin = 0");
      // 等待图表数据查询完成
      const scatterGraphData = await getChartData();
      
      console.log(scatterGraphData);
      // 直接传递对象给EJS模板
      res.render('dashboardAdmin', { user: userResults, scatterGraphData });
    } catch (error) {
      console.error('加载数据失败:', error);
      req.flash('error_msg', '加载用户数据时发生错误。');
      res.redirect('/dashboard');
    }
  } else {
    res.render("dashboard");
  }
}

4. 修正EJS模板的数据注入

在EJS中把图表数据转为JSON字符串,存入canvas的dataset:

<body>
    <div class="container">
        <div class="chart">
            <!-- 用单引号包裹JSON字符串,避免双引号冲突 -->
            <canvas id="myChart" style="width:100%;max-width:700px" data-scatter-data='<%= JSON.stringify(scatterGraphData) %>'></canvas>
        </div>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script>
    <script>
    document.addEventListener('DOMContentLoaded', function () {
            const scatterGraphData = JSON.parse(document.getElementById('myChart').dataset.scatterData);
            
            new Chart("myChart", {
                type: "scatter",
                data: {
                    datasets: [{
                        pointRadius: 4,
                        pointBackgroundColor: "rgb(0,0,255)",
                        data: scatterGraphData
                    }]
                },
                options: {
                    legend: { display: false },
                    scales: {
                        xAxes: [{ ticks: { min: 40, max: 160 } }],
                        yAxes: [{ ticks: { min: 6, max: 16 } }],
                    }
                }
            });
        });
        </script>
</body>

更高效的优化方案

  • 批量查询:用JOIN语句合并多次嵌套查询,将sample、holes、upslopespeed、downslopespeed关联,一次查询获取所有需要的数据,减少数据库请求次数。
  • 缓存数据:如果果岭数据不会实时频繁更新,可使用Redis缓存查询结果,避免重复查询数据库。

内容的提问来源于stack exchange,提问作者Ben Northridge

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 04:07:08