Node.js同步读取文件:异步逻辑致结果数组为空求助
解决Node.js异步读取文件后结果数组为空的问题
嘿,我完全理解你遇到的这个困扰——这是Node.js异步编程里非常典型的新手问题,咱们来一步步拆解并解决它!
问题根源:异步操作的执行顺序
你看到console.log(data)能正常输出每个文件的数据,但console.log(results)却是空数组,核心原因是**fs.readFile是异步非阻塞操作**:
- 当你在
files.forEach里调用fs.readFile时,Node.js会立刻发起文件读取请求,但不会等待读取完成,而是继续执行后续代码; - 所以
forEach循环结束后马上执行的console.log(results),此时所有的文件读取操作还没完成,results自然是空的; - 而
console.log(data)是在每个文件读取完成后的回调里执行的,所以能正确显示数据。
解决方案:等待所有异步操作完成
下面提供两种常用的解决方法,优先推荐第一种现代写法:
方法1:使用async/await + Promise.all(推荐)
这种写法让异步代码看起来像同步代码,可读性更强,也是现在Node.js的主流写法。我们可以用fs.promises.readFile(Node.js 10+支持)把文件读取变成Promise,再用Promise.all等待所有读取操作完成:
const fs = require('fs').promises; // 使用Promise版本的fs模块 const glob = require('glob'); const JSDOM = require('jsdom').JSDOM; async function getData2() { try { // 先把glob的回调转为Promise const files = await new Promise((resolve, reject) => { glob("Massachusetts_*.txt", 'nonull', (err, files) => { if (err) reject(err); else resolve(files); }); }); // 为每个文件创建一个读取并处理的Promise const processingPromises = files.map(async (file) => { // 读取文件,指定utf8编码避免返回Buffer const html = await fs.readFile(file, 'utf8'); const td = [html]; const dom = new JSDOM(`<table><tr>${td.join("")}</tr></table>`); const frag = dom.window.document; const data = [...frag.querySelectorAll("td")].reduce((ob, td) => { const a = td.innerHTML.trim(); ob.push(a); return ob; }, []); console.log(data); return data; }); // 等待所有Promise完成,得到完整的结果数组 const results = await Promise.all(processingPromises); console.log(results); // 现在这里能拿到所有数据了 // 接下来就可以把results存入数据库啦 } catch (err) { console.error("出错了:", err); process.exit(1); } } // 调用异步函数 getData2();
方法2:使用计数器(回调风格)
如果你暂时不想用async/await,可以通过计数器来跟踪所有异步操作的完成状态:
const fs = require('fs'); const glob = require('glob'); const JSDOM = require('jsdom').JSDOM; function getData2() { glob("Massachusetts_*.txt", 'nonull', function (err, files) { if (err) { console.log(err); process.exit(1); } var results = []; let completedCount = 0; // 记录已完成的文件数量 files.forEach(function (file) { fs.readFile(file, function (err, html) { if (err) { console.log("Error reading " + file, err); process.exit(1); } else { const td = [html]; const dom = new JSDOM(`<table><tr>${td.join("")}</tr></table>`); const frag = dom.window.document; const data = [...frag.querySelectorAll("td")].reduce((ob, td) => { const a = td.innerHTML.trim(); ob.push(a); return ob; }, []); console.log(data); results.push(data); completedCount++; // 当所有文件都处理完成时,再执行后续操作 if (completedCount === files.length) { console.log(results); // 这里可以执行存入数据库的逻辑 } } }); }); }); }
总结
Node.js的异步IO特性是它的优势,但刚开始确实容易踩这类顺序问题。记住:不要在异步操作的循环后直接获取结果,必须等待所有异步任务完成后再处理。async/await结合Promise.all是最简洁易维护的方案,建议你多熟悉这种写法哦。
内容的提问来源于stack exchange,提问作者Dev Dev
相关产品推荐
相关产品推荐

