深度遍历目录,怎么在遍历完后转同步
发布于 8 年前 作者 mosikoo 7084 次浏览 来自 问答
// 递归遍历目录
function recursion (filepath) {
	fs.readdir(filepath, (err, files) => {
	 doSomething(); // 这里做一些收集文件信息的操作
	  files.forEach(file => {
		  recursion(file);
	  });
  });
}
第一步:recursion(filepath); 
第二步: doOtherthing();

递归遍历目录,且都是用异步的。递归的过程中会收集一些信息。

怎么样第一步与第二步是同步执行的(ps: 第二步操作主要是处理收集到的所有的信息)

17 回复

刚刚入坑,不太懂,求各位大神指导

var fileNum = 0,filesInfo = []; function recursion (filepath,cb) { fs.readdir(filepath, (err, files) => { if(files && files.length){ fileNum += files.length; doSomething(); // 这里做一些收集文件信息的操作,收集的文件信息放到filesInfo里 files.forEach(file => { recursion(file,cb); }); }else{ if(fileNum === filesInfo.length){ //代表处理所有的目录完毕 cb && typeof cb === ‘function’ && cb(); } } }); } //用法 recursion(filepath, doOtherthing); 这个比较简单,就直接callback了,当然也可以包装一层promise,再加上async await

callback行不通 我想要遍历完目录后完整的获取后所有目录信息后才能执行 doOtherthing() 说白了就是怎么才能判断所有的目录都被遍历了,然后执行doOtherthing()

谢谢回复@yuu2lee4

@mosikoo 我感觉逻辑没错啊,fileNum === filesInfo.length 不就是所有的目录都被遍历了

兄弟,你这个判断条件用于判断遍历是否结束感觉完全不对啊

if(fileNum === filesInfo.length){	
	cb && typeof cb === ‘function’ && cb();
}

filesIfno.lenght与fileNum是一直相等的。 所以遇到空的文件夹或者文件就会触发一次callback @yuu2lee4

@mosikoo 不会一直相等啊 fileNum是目录下有子目录的时候 马上就增加的。。。而filesInfo是在你recursion的时候 收集完信息才去增加的。。是个异步的过程,所以除非你收集完所有的目录信息,否则filesInfo.length是会比fileNum小的

理是这个理,但是运行的时候却不是这样,我把你的代码简化后运行下

const fs = require('fs');
const path = require('path');
var fileNum = 0,filesInfo = [];

const filepath = path.resolve(process.cwd(), process.argv[2]);
function recursion (filepath) {
	fs.readdir(filepath, (err, files) => {
	 	if(files && files.length){
			fileNum += files.length;
			files.forEach(file => {
        const innerFilepath = path.resolve(filepath,file);
        fs.stat(innerFilepath, (err, stats) => {
          // doSomething(); // 这里做一些收集文件信息的操作,收集的文件信息放到filesInfo里
          filesInfo.push('fileInfo');
          recursion(innerFilepath);
        });
	});
	}else{
      console.log( fileNum, filesInfo.length, filepath)
			if(fileNum === filesInfo.length){	//代表处理所有的目录完毕
        console.log('success')
			}
		}
	});
}

recursion(filepath);

这段代码应该能代表你的思想吧。 要不你复制下来随便找个目录运行下看下结果 再次表示感谢@yuu2lee4

之前有一个类似的需求,也是遍历目录,然后实在没想到解法,就用readdirSync把程序写出同步的了

@sirqiao 怒了 用promise async吧,前提是你需要将readdir和stat promise化(找个promisify的包)。。。

async function recursion (filepath) {
    let [files,stat] = await Promise.all([fs.readdir(filepath), fs.stat(filepath)]);
    let res = [stat];
    if(files && files.length){
        const promises = files.map(function (file) {
            const innerFilepath = path.resolve(filepath,file);
            return recursion(innerFilepath);
        });
        const stats = await Promise.all(promises);
        for(let item of stats){
            res = res.concat(item);
        }
    }
    return res;
}

最后大概就是这个样子

自用的一个 readdirp 的部分截取

'use strict';

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

const _ = require('lodash');

const ignore = require('./ignore.js');

function readDirectory(_path, ignorePattenList, callback) {
    if (typeof ignorePattenList === 'function') {
        callback = ignorePattenList;
        ignorePattenList = [];
    }

    let result = {
        path: _path,
        name: path.basename(_path),
        type: 'directory'
    }; 

    fs.readdir(_path, function (error, files) {
        if (error) {
            return callback(error);
        }

        _.remove(files, function (file) {
            return ignore(file, ignorePattenList);
        });

        let length = files.length;
        if (length <= 0) {
            return callback(undefined, result);
        }

        result.children = [];

        files.forEach(function (file) {
            let filePath = path.resolve(_path, file);
            fs.stat(filePath, function (error, fileStat) {
                if (error) {
                    return callback(error);
                }

                if (fileStat.isDirectory()) {
                    readDirectory(filePath, ignorePattenList, function (error, _result) {
                        if (error) {
                            return callback(error);
                        }

                        result.children.push(_result);

                        length -= 1;
                        if (length <= 0) {
                            return callback(undefined, result);
                        }
                    });
                } else {
                    result.children.push({
                        path: filePath,
                        name: file,
                        type: 'file'
                    });

                    length -= 1;
                    if (length <= 0) {
                        return callback(undefined, result);
                    }
                }
            });
        });
    });
}

module.exports = readDirectory;

let targetPath = '/Users/fei/code/';
readDirectory(targetPath, ['.*', 'node_modules'], function (error, tree) {
    // 已经读取所有文件,做接下来想要做的事情
    console.log(JSON.stringify(tree, undefined, 4));
});

不懂,放在forEach后面执行不就行了?建议使用async await异步解决方案,或者promise 自豪地采用 CNodeJS ionic

@DuanPengfei 6666, 实现了@yuu2lee4 说的第一种callback方法。

@yuu2lee4 膜拜大神。 最后一个问题。4.X版本的node想要兼容promise与async要引入哪些插件。

@mosikoo 这个你搜一下或者请教专业的node开发人员来回答吧。。。我主要还是做前端的。。。^_^。。。我知道promise可以用bluebird吧。。async好像就有叫async的包,然后用babel转也可以。。。官方的api可以用promisify转为promise的api

readdirSync 啊。

回到顶部