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

NodeJS中如何从MySQL异步加载数据到User类对象并避免竞态条件

问题本质

你遇到的是Node.js异步IO的典型执行顺序问题,而非竞态条件:db.query默认是回调式异步API,调用后不会阻塞主线程后续代码执行,程序会先执行db.end()和return true逻辑,等数据库IO完成返回结果后,才会执行回调内的属性赋值代码,这就是你拿到返回值后属性还是undefined的原因。

解决方案

可以通过async/await完美解决该问题,主流的MySQL驱动(mysql、mysql2)均支持promise适配,改造方案如下:

注意事项

  • 原有代码中每次调用Load都执行db.connect()和db.end()的逻辑不合理,频繁创建销毁数据库连接会严重降低性能,推荐使用数据库连接池复用连接
  • 直接拼接SQL字符串存在SQL注入风险,必须使用参数化查询传递查询条件

改造后的代码示例

1. 数据库配置(db.js,使用mysql2的promise API,推荐)

const mysql = require('mysql2/promise');
// 创建数据库连接池
const pool = mysql.createPool({
  host: '数据库主机地址',
  user: '数据库用户名',
  password: '数据库密码',
  database: '数据库名称',
  connectionLimit: 10
});
module.exports = pool;

2. User类改造

const db = require("./db");

class User {
    constructor(loader){
        this.id = loader?.id;
        this.email = loader?.email;
        this.name = loader?.name;
    }

    // 添加async修饰符,方法返回Promise对象
    async Load(id = null) {
        // 使用参数化查询避免SQL注入
        const sql = `select usrID, usrEmail, usrName from tblUsers where usrEmail = ?`;
        let connection;
        try {
            // 从连接池获取连接
            connection = await db.getConnection();
            // await等待查询结果返回
            const [results] = await connection.query(sql, [id]);
            if (results.length === 0) {
                // 未查询到对应用户返回false
                return false;
            }
            // 赋值属性
            this.id = results[0].usrID;
            this.email = results[0].usrEmail;
            this.name = results[0].usrName;
            return true;
        } catch (error) {
            throw error;
        } finally {
            // 释放连接回连接池
            if (connection) connection.release();
        }
    }
}

// 异步方法调用需要使用await,在顶层调用可以用立即执行async函数包裹
(async () => {
    const Robbie = new User();
    if(await Robbie.Load("rob@example.com")){
        // 此时属性已经完成赋值,可以正常打印
        console.log("Robbie's ID is", Robbie.id);
    }
})();

兼容旧mysql库的方案

如果你使用的是旧版本的mysql库不想更换为mysql2,可以使用Node.js内置的util.promisify将回调式API转换为promise版本:

const db = require("./db");
const { promisify } = require('util');
// 将query方法包装为返回promise的函数
db.query = promisify(db.query);

// 后续User类的async/await写法和上述示例一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 22:09:03