Tôi muốn đưa ra một giải pháp khác sử dụng tốc độ và hiệu quả của mô hình lập trình ở cốt lõi của Node: sự kiện.
Mọi thứ bạn có thể làm với Promises hoặc mô-đun được thiết kế để quản lý điều khiển luồng, chẳng hạn như async, có thể được thực hiện bằng cách sử dụng các sự kiện và một máy trạng thái đơn giản, mà tôi tin rằng cung cấp một phương pháp luận, có lẽ, dễ hiểu hơn các tùy chọn khác.
Ví dụ: giả sử bạn muốn tính tổng độ dài của nhiều tệp song song:
const EventEmitter = require('events').EventEmitter;
// simple event-driven state machine
const sm = new EventEmitter();
// running state
let context={
tasks: 0, // number of total tasks
active: 0, // number of active tasks
results: [] // task results
};
const next = (result) => { // must be called when each task chain completes
if(result) { // preserve result of task chain
context.results.push(result);
}
// decrement the number of running tasks
context.active -= 1;
// when all tasks complete, trigger done state
if(!context.active) {
sm.emit('done');
}
};
// operational states
// start state - initializes context
sm.on('start', (paths) => {
const len=paths.length;
console.log(`start: beginning processing of ${len} paths`);
context.tasks = len; // total number of tasks
context.active = len; // number of active tasks
sm.emit('forEachPath', paths); // go to next state
});
// start processing of each path
sm.on('forEachPath', (paths)=>{
console.log(`forEachPath: starting ${paths.length} process chains`);
paths.forEach((path) => sm.emit('readPath', path));
});
// read contents from path
sm.on('readPath', (path) => {
console.log(` readPath: ${path}`);
fs.readFile(path,(err,buf) => {
if(err) {
sm.emit('error',err);
return;
}
sm.emit('processContent', buf.toString(), path);
});
});
// compute length of path contents
sm.on('processContent', (str, path) => {
console.log(` processContent: ${path}`);
next(str.length);
});
// when processing is complete
sm.on('done', () => {
const total = context.results.reduce((sum,n) => sum + n);
console.log(`The total of ${context.tasks} files is ${total}`);
});
// error state
sm.on('error', (err) => { throw err; });
// ======================================================
// start processing - ok, let's go
// ======================================================
sm.emit('start', ['file1','file2','file3','file4']);
Cái nào sẽ xuất ra:
start: bắt đầu xử lý 4 đường dẫn
forEachPath: bắt đầu 4 chuỗi quy trình
readPath: file1
readPath: file2
processContent: file1
readPath: file3
processContent: file2
processContent: file3
readPath: file4
processContent: file4
Tổng 4 tệp là 4021
Lưu ý rằng thứ tự của các nhiệm vụ chuỗi quy trình phụ thuộc vào tải hệ thống.
Bạn có thể hình dung luồng chương trình như sau:
start -> forEachPath - + -> readPath 1 -> processContent 1 - + -> done
+ -> readFile 2 -> processContent 2 - +
+ -> readFile 3 -> processContent 3 - +
+ -> readFile 4 -> processContent 4 - +
Để sử dụng lại, sẽ rất đơn giản nếu tạo một mô-đun để hỗ trợ các mẫu điều khiển luồng khác nhau, tức là loạt, song song, hàng loạt, trong khi, cho đến khi, v.v.