我构建节点脚本异步输出目录的文件数和行数;但是,我的异步控制流程出了问题。
// 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行
...等
答案 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);
}
}