Node.js使用异步fs递归遍历目录将文件名存入数组的问题
问题根因
代码无法正确收集全量文件的核心原因是递归调用异步函数时没有等待执行完成:
reader是async声明的异步函数,调用后会返回Promise对象- 你在
items.map遍历中遇到目录时直接调用reader()遍历子目录,但既没有给这个递归调用加await,也没有等待所有递归生成的Promise状态变更 - 外层
reader跑完当前目录的遍历逻辑就直接结束返回了,此时子目录的文件读取、文件名入栈操作还在异步任务队列里没执行,最后.then回调触发时,子目录的文件自然没被收集到数组里。
修复方法
核心要做的就是等所有子目录的递归异步任务全部执行完,再结束当前层的reader函数,另外建议用内置path模块拼接路径,避免跨系统路径分隔符不兼容的问题。
修复原有全局数组的写法
import { readdir } from 'fs/promises'; import path from 'path'; const arr = []; const reader = async (dir = './src') => { try { const items = await readdir(dir, { withFileTypes: true }); // 等待当前目录下所有遍历任务(含子目录递归)全部执行完成 await Promise.all( items.map(async (item) => { const itemPath = path.join(dir, item.name); if (item.isDirectory()) { // 递归时加await,等子目录遍历完成再继续 await reader(itemPath); } else { arr.push(item.name); // 如果需要存文件完整路径,就改成arr.push(itemPath) } }) ); } catch (err) { console.log(err); } }; reader().then(() => { console.log(arr); });
更优雅的无全局变量写法
不需要在函数外维护存储数组,直接让递归函数返回当前目录收集到的所有文件,最后汇总结果即可:
import { readdir } from 'fs/promises'; import path from 'path'; const reader = async (dir = './src') => { const result = []; try { const items = await readdir(dir, { withFileTypes: true }); // 批量执行当前目录下所有遍历任务 await Promise.all( items.map(async (item) => { const itemPath = path.join(dir, item.name); if (item.isDirectory()) { // 拿到子目录的文件列表,合并到当前结果 const subFiles = await reader(itemPath); result.push(...subFiles); } else { result.push(item.name); } }) ); } catch (err) { console.log(err); } return result; }; reader().then(fileList => { console.log(fileList); });
注意事项
- 异步函数调用如果存在时序依赖,必须加
await等待执行完成,漏写await会导致异步任务脱离预期执行顺序 - 数组的
map/forEach等遍历方法本身不会等待异步回调,遍历异步任务时必须配合Promise.all收集所有Promise后统一等待 - 路径拼接不要手动写
/,用Node内置path.join()方法自动适配不同操作系统的路径规则,减少隐性bug
内容的提问来源于stack exchange,提问作者user19419899
相关产品推荐
相关产品推荐

