Promise递归树路径执行顺序问题:目录遍历代码输出顺序不符预期
为什么目录遍历的输出顺序不符合预期?
你猜的没错!这个顺序问题正是Promise.all的并行执行特性导致的,咱们来一步步拆解原因,再看看怎么修复。
问题根源:Promise.all是并行执行的
你的代码里用了Promise.all((await readdir(dir)).map(async file => {...})),这里的逻辑存在一个关键特性:
- 先读取当前目录下的所有文件/子目录,得到一个数组
- 用
map把每个文件转换成一个异步处理的Promise - 然后
Promise.all会同时启动所有这些Promise,等待它们全部完成
这就意味着,根目录下的directoryA和directoryB的处理是并行进行的:
- 同时对
directoryA和directoryB执行stat判断是否为目录 - 假设
directoryB的stat操作更快完成,它会先被push到acc数组里 - 而
directoryA的子目录遍历是在它自己的stat完成后才启动的异步操作,这时候directoryB已经被加入数组了,所以最终顺序就变成了directoryA→directoryB→directoryA/subdirectoryC
修复方案:串行处理目录项
如果要保证「先处理完当前目录的一个项(包括它的所有子目录),再处理下一个项」的顺序,咱们需要把并行执行改成串行执行,用for...of循环代替map + Promise.all就可以做到:
const { join } = require('path'); const { promisify } = require('util'); const fs = require('fs'); const readdir = promisify(fs.readdir); const stat = promisify(fs.stat); async function $readDir(dir, acc = []) { const files = await readdir(dir); // 用for...of逐个处理每个文件,严格串行执行 for (const file of files) { const fullPath = join(dir, file); const stats = await stat(fullPath); if (stats.isDirectory()) { acc.push(fullPath); // 等待当前子目录的遍历完全完成,再处理下一个文件 await $readDir(fullPath, acc); } } return acc; }
为什么这个方案能解决问题?
for...of循环会严格按照数组的顺序迭代每个文件:
- 先处理
directoryA:判断是目录→加入数组→递归遍历它的所有子目录,直到directoryA的全部子目录都处理完毕 - 再处理
directoryB:重复相同流程,这样最终的顺序就会是directoryA→directoryA/subdirectoryC→directoryB,完全符合你的预期。
内容的提问来源于stack exchange,提问作者FJsanto
相关产品推荐
相关产品推荐

