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

Express中使用fs.readdir无法更新外部变量的问题求助

Express中使用fs.readdir无法更新外部变量的问题求助

兄弟,你遇到的这个问题其实是Node.js里异步代码执行顺序的经典坑!我来给你掰扯清楚:

问题根源

fs.readdir是一个异步非阻塞的API,它不会等文件读取完成再往下走代码。你的代码执行顺序其实是这样的:

  1. 定义CollectionsHtml和mdFiles
  2. 调用fs.readdir,但它不会立刻执行里面的回调函数,而是先把这个任务丢到事件队列里
  3. 直接往下跑后面的for循环(这时候mdFiles还是空数组,所以循环根本不会执行)
  4. 执行CollectionsHtml.concat(...)、console.log(mdFiles)这些代码
  5. 最后才轮到fs.readdir的回调函数执行,这时候才会往mdFiles里push内容,但这时候前面的代码都已经跑完了,自然拿不到更新后的值

另外还有个小细节:concat方法不会修改原字符串,它会返回一个新字符串!你之前用CollectionsHtml.concat(...)其实没起作用,应该用+=来修改原变量。

解决方案(给你三个常用的路子)

方案1:把所有依赖异步结果的代码丢到回调里

最简单的改法,就是把拼接HTML、发送响应的逻辑都放到fs.readdir的回调函数里面,这样就能保证等文件读取完成后再处理:

app.get('/collections', (req, res) => {
    let CollectionsHtml = `
    <!DOCTYPE html>
    <head>
    <link href="style.css" rel="stylesheet" type="text/css">
    </head>
    <body>
        <div id="container">
            <div id="header">
                <h1>Collections</h1>
            </div>
            <div id="sidebar">
                <a href="/">Home</a>
            </div>
            <div id="collections">`;
    let mdFiles = [];
    fs.readdir(markdownDir, (err, files) => {
        if (err) {
            // 别忘了加错误处理!比如返回500页面
            res.status(500).send('读取文件失败');
            return;
        }
        files.forEach(file => {
            const informalName = file.split(".")[0];
            mdFiles.push(informalName);
            console.log(`loaded ${file} in /collections`);
            // 这里直接拼接HTML片段
            CollectionsHtml += `<div id="article">
                        <a href="/collections/${informalName}">${informalName}</a>
                    </div>`;
        });
        // 拼接HTML结尾
        CollectionsHtml += `</div >
        </div >
    </body >
        <script type="text/javascript src=""></script>
    `;
    console.log(mdFiles.join(""));
    console.log(CollectionsHtml);
    res.send(CollectionsHtml);
    });
});

方案2:用同步版本的fs.readdirSync

如果你的场景对性能要求不高(比如读取的文件不多),可以用同步阻塞的fs.readdirSync,这样代码顺序就和你预期的一样了:

app.get('/collections', (req, res) => {
    let CollectionsHtml = `
    <!DOCTYPE html>
    <head>
    <link href="style.css" rel="stylesheet" type="text/css">
    </head>
    <body>
        <div id="container">
            <div id="header">
                <h1>Collections</h1>
            </div>
            <div id="sidebar">
                <a href="/">Home</a>
            </div>
            <div id="collections">`;
    let mdFiles = [];
    try {
        // 同步读取,会等读取完成再往下走
        const files = fs.readdirSync(markdownDir);
        files.forEach(file => {
            const informalName = file.split(".")[0];
            mdFiles.push(informalName);
            console.log(`loaded ${file} in /collections`);
            CollectionsHtml += `<div id="article">
                        <a href="/collections/${informalName}">${informalName}</a>
                    </div>`;
        });
    } catch (err) {
        res.status(500).send('读取文件失败');
        return;
    }
    CollectionsHtml += `</div >
        </div >
    </body >
        <script type="text/javascript src=""></script>
    `;
    console.log(mdFiles.join(""));
    console.log(CollectionsHtml);
    res.send(CollectionsHtml);
});

方案3:用Promise+async/await(更现代的写法)

Node.js的fs模块从v10开始有Promise版本,用async/await写起来更清爽,不会有回调嵌套:

// 先引入promise版的fs
const fs = require('fs').promises;

app.get('/collections', async (req, res) => {
    let CollectionsHtml = `
    <!DOCTYPE html>
    <head>
    <link href="style.css" rel="stylesheet" type="text/css">
    </head>
    <body>
        <div id="container">
            <div id="header">
                <h1>Collections</h1>
            </div>
            <div id="sidebar">
                <a href="/">Home</a>
            </div>
            <div id="collections">`;
    let mdFiles = [];
    try {
        const files = await fs.readdir(markdownDir);
        files.forEach(file => {
            const informalName = file.split(".")[0];
            mdFiles.push(informalName);
            console.log(`loaded ${file} in /collections`);
            CollectionsHtml += `<div id="article">
                        <a href="/collections/${informalName}">${informalName}</a>
                    </div>`;
        });
    } catch (err) {
        res.status(500).send('读取文件失败');
        return;
    }
    CollectionsHtml += `</div >
        </div >
    </body >
        <script type="text/javascript src=""></script>
    `;
    console.log(mdFiles.join(""));
    console.log(CollectionsHtml);
    res.send(CollectionsHtml);
});

额外提醒

一定要加错误处理!不管用哪种方案,都要捕获readdir可能出现的错误(比如目录不存在、没有权限),不然服务器遇到异常可能会直接崩掉。

备注:内容来源于stack exchange,提问作者emkawley

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 13:38:04