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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 16:56:14