Node.js中fs.readFile异步执行导致JSON加载顺序异常问题
解决Node.js中fs.readFile异步导致的执行顺序问题
问题根源在于fs.readFile是异步非阻塞API:调用它后程序会立即继续执行后续代码,不会等待文件读取完成。所以你判断file_data是否为空的逻辑先执行,此时数据还未加载,自然输出"Data not loaded.",等文件读取完成后才会执行回调里的打印逻辑。
下面是几种可行的修正方案:
方案1:使用回调函数调整执行顺序
把依赖文件数据的逻辑放到fs.readFile的回调中,确保数据加载完成后再处理:
const fs = require('fs'); class Game { constructor() { this.file_data = null; // 传入回调函数,等数据加载完毕再执行判断逻辑 this.loadDefs(() => { if (!this.file_data) { console.log("Data not loaded."); } else { console.log("Data loaded successfully:", this.file_data); // 这里编写所有需要使用file_data的业务逻辑 } }); } loadDefs(callback) { fs.readFile('./data.json', 'utf8', (err, data) => { if (err) { console.error("读取文件失败:", err); callback(); return; } this.file_data = JSON.parse(data); console.log(this.file_data); callback(); }); } } new Game();
方案2:使用Promise + async/await(推荐)
用Node.js提供的Promise版本fs.promises,配合async/await让异步代码看起来像同步代码,更易维护:
const fs = require('fs').promises; class Game { constructor() { this.file_data = null; // 构造函数不能直接标记为async,用立即执行的async函数包裹初始化逻辑 (async () => { await this.loadDefs(); this.checkData(); })(); } async loadDefs() { try { const data = await fs.readFile('./data.json', 'utf8'); this.file_data = JSON.parse(data); console.log(this.file_data); } catch (err) { console.error("读取文件失败:", err); } } checkData() { if (!this.file_data) { console.log("Data not loaded."); } else { console.log("Data loaded successfully:", this.file_data); // 后续业务逻辑写在这里 } } } new Game();
如果觉得构造函数里的立即执行async函数不够优雅,可以把初始化逻辑抽成单独方法,外部调用时处理异步:
const fs = require('fs').promises; class Game { constructor() { this.file_data = null; } async init() { await this.loadDefs(); this.checkData(); } async loadDefs() { try { const data = await fs.readFile('./data.json', 'utf8'); this.file_data = JSON.parse(data); console.log(this.file_data); } catch (err) { console.error("读取文件失败:", err); } } checkData() { if (!this.file_data) { console.log("Data not loaded."); } else { console.log("Data loaded successfully:", this.file_data); } } } // 外部调用 (async () => { const game = new Game(); await game.init(); })();
关于你尝试的"函数外读取文件再传入"为何失败
如果你的写法是先调用fs.readFile,然后立刻创建Game实例并传入未加载完成的变量,那肯定会失败——因为异步读取还没完成,变量还是空值。正确的写法应该是等文件读取完成后再创建实例:
const fs = require('fs'); fs.readFile('./data.json', 'utf8', (err, data) => { if (err) throw err; const file_data = JSON.parse(data); const game = new Game(file_data); // 此时数据已完全加载,可正常传入 }); class Game { constructor(file_data) { this.file_data = file_data; if (!this.file_data) { console.log("Data not loaded."); } else { console.log("Data loaded successfully:", this.file_data); } } }
核心总结
所有依赖异步数据的逻辑,必须等待异步操作完成后再执行,这是Node.js异步编程的核心原则。根据项目复杂度选择回调、Promise或async/await即可解决执行顺序问题。
内容的提问来源于stack exchange,提问作者MisterZig
相关产品推荐
相关产品推荐

