Node.js中mysql查询结果在函数外访问返回undefined问题
问题本质
这不是作用域问题,是JavaScript事件循环机制下的异步执行时序错误:你在外部访问rows的时机,远早于SQL查询返回结果、给rows赋值的时机,所以拿到的永远是初始值undefined。
执行顺序拆解
JS运行时会先把所有同步代码执行完,再轮询处理异步队列里的任务,你的代码实际执行流是这样的:
- 同步执行阶段(主线程阻塞执行,所有异步任务都在排队等待)
- 声明全局变量
rows、fields,初始值为undefined - 创建数据库连接实例,注意这一步只是生成实例对象,并没有完成和MySQL的TCP连接握手
- 调用
connection.connect(),传入的连接回调不会立刻执行,会被暂存,等连接握手完成后才会进入异步任务队列 - 声明异步函数
query - 执行你写在外部的
console.log(rows):这时候没有任何代码给rows赋值,直接打印初始值undefined - 调用
query():函数执行到await connection.execute(...)时会暂停自身执行,把SQL请求发出去,后续给rows赋值、函数内打印rows的逻辑全部被塞进微任务队列,等SQL结果返回后才会执行
- 声明全局变量
- 异步任务处理阶段(同步代码全部执行完才会走到这步)
- 数据库连接握手完成,触发connect回调,打印
logged in! - SQL查询结果返回,恢复
query函数执行:把查询结果赋值给全局的rows、fields,执行函数内的console.log(rows),这时候才能打印正确结果
- 数据库连接握手完成,触发connect回调,打印
简单说:你写在函数外的打印语句是同步任务,优先级比所有异步逻辑高,执行的时候rows还根本没被赋值,当然拿不到结果。你就算用了async/await,只要访问变量的代码写在await逻辑的时序前面,就不可能拿到值。
另外你的代码还有个隐藏问题:connection.connect是异步回调,你没有等连接成功就调用query,实际运行时可能出现连接还没建立就发查询请求的报错。
正确写法
不要用全局变量传递异步结果,所有依赖查询结果的逻辑,都要保证在await拿到结果之后执行:
// 注意要使用mysql2的promise版本,原生mysql包不支持await写法 const mysql = require('mysql2/promise'); async function run() { // 等待连接建立完成 const connection = await mysql.createConnection({ host: 'localhost', user: 'root', password: '', database: 'root' }); console.log('logged in!'); // 等待查询完成,直接拿到结果 const [rows, fields] = await connection.execute("SELECT plan FROM users WHERE username = 'onyx'"); // 从这行开始,所有访问rows的代码都能拿到正确值 console.log(rows); // 后续业务逻辑直接写在这里即可,需要复用rows就作为参数传递给其他函数,不要靠全局变量存储 await connection.end(); } // 捕获全局异步错误 run().catch(err => { console.error(err); process.exit(1); });
关键误区提醒
- async/await是异步语法糖,不会把异步操作变成同步阻塞代码,只有
await关键字后面的代码会等待异步结果,await所在函数外部的同步代码不会等待 - 永远不要在同步代码流里读取异步操作返回的结果,时序上永远不可能读到
- 回调、Promise、async/await只是异步代码的不同写法,不会改变JS本身先同步后异步的执行逻辑
内容的提问来源于stack exchange,提问作者Onyx
相关产品推荐
相关产品推荐

