实现代码
const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
// 将 fs.readFile 转化为 Promise 形式
const readFileAsync = promisify(fs.readFile);
// 假设要读取的文件路径数组
const filePaths = [
path.join(__dirname, 'file1.txt'),
path.join(__dirname, 'file2.txt'),
path.join(__dirname, 'file3.txt')
];
async function readFilesConcurrently() {
try {
// 使用 Promise.all 并发读取文件
const results = await Promise.all(filePaths.map(filePath => readFileAsync(filePath, 'utf8')));
// 汇总处理结果
const summary = results.join(' ');
console.log('汇总结果:', summary);
return summary;
} catch (error) {
console.error('读取文件时出错:', error);
}
}
readFilesConcurrently();
好处
- 提高效率:通过并发执行多个 I/O 操作,充分利用系统资源,减少等待时间,大大提高程序整体执行效率。比如在读取多个文件时,无需一个一个顺序读取,而是同时发起多个读取请求。
- 代码可读性增强:使用 Promise 和 async/await 使得异步代码看起来更像同步代码,逻辑更加清晰。相比于传统的回调函数嵌套(回调地狱),代码结构更易于理解和维护。例如在上述代码中,
await
使得代码等待 Promise 被解决后再继续执行,符合人类阅读和编写同步代码的习惯。
- 错误处理方便:通过
try...catch
块可以统一捕获和处理所有并发操作中抛出的错误,而不像回调函数那样每个回调都要单独处理错误。在上述代码中,try...catch
可以捕获 Promise.all
中任何一个 readFileAsync
操作抛出的错误。
可能遇到的问题
- 内存消耗:如果并发的 I/O 操作过多,可能会导致内存消耗过大。例如同时读取大量大文件,会占用较多内存用于存储读取的数据。这可能会导致程序性能下降甚至内存溢出。
- 资源竞争:在并发操作中,如果多个操作同时访问和修改共享资源(虽然在读取文件场景中较少出现,但在其他涉及共享资源的 I/O 操作中可能出现),可能会导致数据不一致等问题。需要使用锁机制或其他同步手段来避免资源竞争。
- 调试困难:尽管 Promise 和 async/await 使代码更易读,但并发操作本身的复杂性可能会使调试变得困难。例如难以确定错误具体发生在哪个并发操作中,因为所有操作都在同时执行。