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

JavaScript递归函数中Promise的正确使用:实现目录遍历后输出完成提示

修正递归目录遍历Promise的resolve位置,实现预期输出顺序

你的问题核心是没有等待所有异步遍历操作完成就提前resolve了Promise。原代码里,只要遇到第一个文件(app.js)就调用resolve,这会直接触发.then()里的completed输出,而此时子目录的遍历还在后台异步执行,所以后续的temp1.js、temp2.js会在completed之后才打印出来。

要实现所有文件输出完成后再打印completed,我们需要:

  • 收集所有文件/子目录处理的异步任务(Promise)
  • 等待所有任务完成后,再统一resolve整个readDirectory的Promise

修正后的代码(推荐使用async/await简化写法)

const fs = require('fs').promises;
const path = require('path');

let readDirectory = async function(dirname) {
  const files = await fs.readdir(dirname);
  // 收集每个文件的处理任务为Promise
  const tasks = files.map(async (file) => {
    const filePath = path.join(dirname, file);
    const stats = await fs.stat(filePath);
    if (stats.isDirectory()) {
      // 递归遍历子目录,等待子目录处理完成
      await readDirectory(filePath);
    } else {
      // 打印当前文件名
      console.log(file);
    }
  });
  // 等待当前目录下所有任务(文件+子目录)执行完成
  await Promise.all(tasks);
};

// 调用并处理完成后的逻辑
readDirectory(sampledir)
  .then(() => console.log('completed'))
  .catch(err => console.error('遍历出错:', err));

关键改动说明

  1. 改用fs.promises:避免嵌套回调地狱,代码结构更清晰易读。
  2. 用map替代forEach:forEach无法等待异步操作结束,map可以将每个文件的处理转化为Promise并存入数组,方便后续统一等待。
  3. Promise.all统一等待:确保当前目录下的所有文件、子目录都遍历完成后,才会让整个readDirectory的Promise进入resolved状态。
  4. 移除过早的resolve:原代码中单个文件处理时就调用resolve的逻辑被移除,改为等待所有任务完成后,由async函数自动返回resolved的Promise。

如果你坚持使用原始的Promise回调写法,也可以这样调整:

const fs = require('fs');
const path = require('path');

let readDirectory = function(dirname){
  return new Promise((resolve,reject)=>{
    fs.readdir(dirname,(err,files)=>{
      if(err) reject(err);
      // 收集所有文件处理的Promise
      const promises = files.map(file=>{
        return new Promise((innerResolve)=>{
          const filePath = path.join(dirname, file);
          fs.stat(filePath,(err,stats)=>{
            if(stats.isDirectory()){
              // 递归遍历子目录,等待其完成后再标记当前任务结束
              readDirectory(filePath).then(innerResolve);
            }else{
              console.log(file);
              innerResolve();
            }
          })
        })
      });
      // 所有任务完成后,再resolve外层Promise
      Promise.all(promises).then(resolve).catch(reject);
    })
  })
}

readDirectory(sampledir).then(()=>console.log('completed')).catch(err=>console.error(err));

修改后,输出顺序会完全符合你的预期:

app.js
temp1.js
temp2.js
completed

内容的提问来源于stack exchange,提问作者Krishna Chaitanya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:25:25