使用promises和recursion迭代文件目录

use*_*195 3 javascript recursion node.js promise

我知道我在以下函数中提前返回,如何将递归promises链接到我的结果?

我的目标是获取目录中所有文件列表及其所有子目录.数组是单维的,我在这个例子中使用了concat.

function iterate(body) {
    return new Promise(function(resolve, reject){
        var list = [];
        fs.readdir(body.path, function(error, list){
            list.forEach(function(file){
                file = path.resolve(body.path, file);
                fs.stat(file, function(error, stat){
                    console.log(file, stat.isDirectory());
                    if(stat.isDirectory()) {
                        return iterate({path: file})
                            .then(function(result){
                                list.concat(result);
                            })
                            .catch(reject);
                    } else {
                        list.push(file);
                    }
                })
            });
            resolve(list);
        });
    });
};
Run Code Online (Sandbox Code Playgroud)

jfr*_*d00 11

您的代码中存在许多错误.部分清单:

  1. .concat()返回一个新数组,因此list.concat(result)它本身并没有做任何事情.

  2. 您正在resolve()同步调用而不是等待所有异步操作完成.

  3. 您试图以递归方式从几个嵌套的异步回调中深入返回.你不能这样做.这不会让结果回到任何地方.

通过使用fs模块的promisified版本,我发现这更容易使用.我使用Bluebird创建它,然后你可以这样做:

const path = require('path');
var Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

function iterate(dir) {
    return fs.readdirAsync(dir).map(function(file) {
        file = path.resolve(dir, file);
        return fs.statAsync(file).then(function(stat) {
            if (stat.isDirectory()) {
                return iterate(file);
            } else {
                return file;
            }
        })
    }).then(function(results) {
        // flatten the array of arrays
        return Array.prototype.concat.apply([], results);
    });
}
Run Code Online (Sandbox Code Playgroud)

注意:我改为iterate()只采取初始路径,因此它更通用.您body.path最初可以通过它来适应.


这是使用通用ES6承诺的版本:

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

fs.readdirAsync = function(dir) {
    return new Promise(function(resolve, reject) {
        fs.readdir(dir, function(err, list) {
            if (err) {
                reject(err);
            } else {
                resolve(list);
            }
        });
    });
}

fs.statAsync = function(file) {
    return new Promise(function(resolve, reject) {
        fs.stat(file, function(err, stat) {
            if (err) {
                reject(err);
            } else {
                resolve(stat);
            }
        });
    });
}


function iterate2(dir) {
    return fs.readdirAsync(dir).then(function(list) {
        return Promise.all(list.map(function(file) {
            file = path.resolve(dir, file);
            return fs.statAsync(file).then(function(stat) {
                if (stat.isDirectory()) {
                    return iterate2(file);
                } else {
                    return file;
                }
            });
        }));
    }).then(function(results) {
        // flatten the array of arrays
        return Array.prototype.concat.apply([], results);
    });
}

iterate2(".").then(function(results) {
    console.log(results);
});
Run Code Online (Sandbox Code Playgroud)

这是一个添加可自定义过滤功能的版本:

function iterate2(dir, filterFn) {
    // default filter function accepts all files
    filterFn = filterFn || function() {return true;}
    return fs.readdirAsync(dir).then(function(list) {
        return Promise.all(list.map(function(file) {
            file = path.resolve(dir, file);
            return fs.statAsync(file).then(function(stat) {
                if (stat.isDirectory()) {
                    return iterate2(file, filterFn);
                } else {
                    return filterFn(file)? file : "";
                }
            });
        })).then(function(results) {
            return results.filter(function(f) {
                return !!f;
            });
        });
    }).then(function(results) {
        // flatten the array of arrays
        return Array.prototype.concat.apply([], results);
    });
}

// example usage
iterate2(".", function(f) {
    // filter out 
    return !(/(^|\/)\.[^\/\.]/g).test(f);
}).then(function(results) {
    console.log(results);
});
Run Code Online (Sandbox Code Playgroud)