使用Node FS查找文件数量并获取总行数

时间:2016-12-29 02:33:30

标签: javascript node.js asynchronous promise fs

我构建节点脚本异步输出目录的文件数和行数;但是,我的异步控制流程出了问题。

// Import Dependencies
const fs = require('fs');

const get_dir_line_count = (dir) => {
  let output = { file_count: 0, file_line: 0, path: '' };
  new Promise( (resolve, reject) => {
    fs.readdir(dir, (err, dir_contents) => {
      resolve(dir_contents);
    });
  }).then( (promise_contents) => {
    Promise.all(promise_contents.map( (file) => {
      const file_path = dir + '/' + file;
      return new Promise( (resolve, reject) => {
        fs.stat(file_path, (err, stat) => {
          if(err || file[0] === '.') return err;
          if(stat.isDirectory() && file !== 'node_modules'){
            get_dir_line_count(file_path);
          }
          else if(stat.isFile()){
            promise_line_count(file_path)
              .then( (line_count) => {
                output.path = dir;
                output.file_line += line_count;
                output.file_count++;
                resolve(output);
              });
          };
        });
      }).then( (resolved_output) => {
        console.log(resolved_output)
        return resolved_output;
      });
    }));
  });

};

const promise_line_count = (pathToFile) => {
  let line_count = 0;
  return new Promise( (resolve, reject) => {
    fs.createReadStream(pathToFile)
      .on("data", (buffer) => {
        buffer.forEach( (chunk) => {
          if(chunk === 10) line_count++;
        });
      }).on("end", () => {
        resolve(line_count);
      });
  });
};

const directory = process.argv[2];
get_dir_line_count('./../' + directory);

我的目的是以递归方式遍历输出Promise.all数组的目录。每个数组都是目录计算数据的集合。但是,我在Promise.all上遇到异步控制流问题。如果有人能提供反馈,那将会有所帮助。

输出:

项目= 5个文件,50行

Project / src = 10个文件,60行

Project / apple = 20个文件,200行

...等

2 个答案:

答案 0 :(得分:1)

一个问题是您没有从get_dir_line_count函数本身返回任何内容:

const get_dir_line_count = (dir) => {
  let output = { file_count: 0, file_line: 0, path: '' };
  new Promise( (resolve, reject) => {
// ^---- missing a return statement

另一个问题是您忘记从Promise.all返回结果,因此可以正确构建链:

// ...
}).then( (promise_contents) => {
    Promise.all(promise_contents.map( (file) => {
// ^---- missing a return

您也忘记了递归(或解决)对get_dir_line_count的递归调用:

if(err || file[0] === '.') return err;
  if(stat.isDirectory() && file !== 'node_modules'){
    get_dir_line_count(file_path);
  // ^--- missing a return statement or resolve statement
  }

最后,由于您从get_dir_line_count返回输出对象,因此您可以通过添加then并将结果传递到console.log来检查工作是否有效:

const directory = process.argv[2];
get_dir_line_count('./../' + directory).then(console.log) // <-- get the output object and the log it

就处理异步代码的复杂性而言,清理控制流的主要方法是将单个逻辑提取到单独的函数中。

Bellow你可以找到一个方法的代码示例以及嵌入的注释(我还保留了下划线的命名首选项):

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

// resolves with the file names within the given directory
function get_file_names(dir) {
  return new Promise((resolve, reject) => {
    fs.readdir(dir, (err, fileNames) => {
      if (err) return reject(err);
      resolve(fileNames);
    });
  });
}

// resolves with an object containing the type ('file' or 'dir') for the given file path and the file path itself: { file_path, type }
function get_path_and_type(file_path) {
  return new Promise((resolve, reject) => {
    fs.stat(file_path, (err, stat) => {
      if (err) return reject(err);
      if (!stat.isDirectory() && !stat.isFile()) reject('Invalid Type');
      const type = stat.isDirectory() ? 'dir' : 'file';
      resolve({
        file_path,
        type
      });
    });
  });
}

// same as before, counts lines for the given file path
function count_lines(file_path) {
  return new Promise((resolve, reject) => {
    let lineCount = 0;
    fs.createReadStream(file_path)
      .on("data", (buffer) => {
        buffer.forEach((chunk) => {
          if (chunk === 10) lineCount++;
        });
      }).on("end", () => {
        resolve(lineCount);
      }).on("error", reject);
  });
};

function get_dir_line_count(dir) {

  const output = {
    file_count: 0,
    file_lines: 0,
    path: dir
  };

  // get all filenames in the given directory
  return get_file_names(dir)
    // filter all file names that start with a '.' or include the string 'node_modules'
    .then((names) =>
      names.filter((name) =>
        !name.startsWith('.') && !name.includes('node_modules')
      )
    )
    // map every file name into a promise that resolves with the type for that file name within the given dir
    .then((names) =>
      names.map((name) =>
        get_path_and_type(path.join(dir, name))
        .catch(console.warn) // log invalid typed files if necessary
      )
    ).then((paths_and_types_promises) =>
      Promise.all(paths_and_types_promises.map((promise) =>
        promise.then(({
          file_path,
          type
        }) => {
          if (type === 'dir') {
            // if current file path corresponds to a directory
            // recursive count its files and lines and add it to the overall output
            return get_dir_line_count(file_path)
              .then((recursive_output) => {
                output.file_count += recursive_output.file_count;
                output.file_lines += recursive_output.file_count;
              });
          } else {
            // count the lines for the current file path and then update the overall output
            return count_lines(file_path)
              .then((file_lines) => {
                output.file_lines += file_lines;
                output.file_count += 1;
              })
          }
        })
      ))
    // this last chain makes sure we wait for the promise to resolve
    // and populate the output object before resolving with it
    ).then(() => output);
}

get_dir_line_count(process.argv[2])
  .then(console.log);

答案 1 :(得分:0)

public class MainApp extends Application {

    private Stage primaryStage;
    private AnchorPane rootLayout;

    @Override
    public void start(Stage primaryStage) {
        this.primaryStage = primaryStage;
        this.primaryStage.setTitle("Car Manager");

        // Prevent the window resizing
        this.primaryStage.setResizable(false);

        showLogin();
    }

    /**
     * Show the login page
     */
    public void showLogin() {
        try {
            // Load login page
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(MainApp.class.getResource("view/Login.fxml"));
            AnchorPane login = (AnchorPane) loader.load();

            // Set the scene containing the login page
            Scene scene = new Scene(login);
            primaryStage.setScene(scene);

            // Give the controller access to the main application
            LoginController controller = loader.getController();
            controller.setMainApp(this);

            // Show the scene
            primaryStage.show();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Initializes the root layout
     */
    public void initRootLayout() {
        try {
            // Load root layout
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(MainApp.class.getResource("view/RootLayout.fxml"));
            rootLayout = (AnchorPane) loader.load();

            // Give the controller access to the main application
            RootLayoutController controller = loader.getController();
            controller.setMainApp(this);

            // Get the DrawerContentController 

            // really no point in doing that since you don't display the UI you load...
            // FXMLLoader drawerLoader = new FXMLLoader();
            // drawerLoader.setLocation(MainApp.class.getResource("view/DrawerContent.fxml"));
            // drawerLoader.load(); // Load the fxml file (and do nothing with it)
            // DrawerContentController drawerController = drawerLoader.getController();
            // drawerController.setMainApp(this);

            // Show the scene containing the root layout
            Scene scene = new Scene(rootLayout);
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}