若要查看如何解决问题,请在方法末尾打印console.log。
通常会出错的事情:
- 任意顺序。
- printFiles可以在打印文件之前完成运行。
- 表现不佳。
这些并不总是错误的,但在标准用例中经常如此。
通常,使用forEach将导致除最后一个以外的所有结果。它会在不等待函数的情况下调用每个函数,这意味着它告诉所有函数启动然后在不等待函数完成的情况下完成。
import fs from 'fs-promise'
async function printFiles () {
const files = (await getFilePaths()).map(file => fs.readFile(file, 'utf8'))
for(const file of files)
console.log(await file)
}
printFiles()
这是本机JS中的一个示例,它将保留顺序,防止函数过早返回,并且在理论上保留最佳性能。
这将:
- 初始化所有文件读取以并行发生。
- 通过使用map将文件名映射到promises来保留命令。
- 等待数组定义的顺序中的每个promise。
使用此解决方案,将在第一个文件可用时立即显示它,而不必等待其他文件首先可用。
它还将同时加载所有文件,而不必等待第一个文件完成才可以开始读取第二个文件。
此版本和原始版本的唯一缺点是,如果一次开始多次读取,则由于一次可能发生更多错误,因此更难处理错误。
使用一次读取一个文件的版本,然后将在发生故障时停止,而不会浪费时间尝试读取更多文件。即使使用精心设计的取消系统,也很难避免它在第一个文件上失败,但同时也已经读取了大多数其他文件。
性能并非总是可预测的。尽管许多系统使用并行文件读取会更快,但有些系统会更喜欢顺序读取。有些是动态的,可能在负载下发生变化,提供延迟的优化在争用激烈的情况下并不总是能产生良好的吞吐量。
该示例中也没有错误处理。如果某些事情要求它们要么全部成功显示,要么根本不显示,那就不会了。
建议在每个阶段使用console.log进行深入实验,并使用伪造的文件读取解决方案(改为随机延迟)。尽管许多解决方案在简单情况下似乎都执行相同的操作,但所有解决方案都有细微的差异,因此需要进行额外的审查才能挤出。
使用此模拟可以帮助说明解决方案之间的区别:
(async () => {
const start = +new Date();
const mock = () => {
return {
fs: {readFile: file => new Promise((resolve, reject) => {
// Instead of this just make three files and try each timing arrangement.
// IE, all same, [100, 200, 300], [300, 200, 100], [100, 300, 200], etc.
const time = Math.round(100 + Math.random() * 4900);
console.log(`Read of ${file} started at ${new Date() - start} and will take ${time}ms.`)
setTimeout(() => {
// Bonus material here if random reject instead.
console.log(`Read of ${file} finished, resolving promise at ${new Date() - start}.`);
resolve(file);
}, time);
})},
console: {log: file => console.log(`Console Log of ${file} finished at ${new Date() - start}.`)},
getFilePaths: () => ['A', 'B', 'C', 'D', 'E']
};
};
const printFiles = (({fs, console, getFilePaths}) => {
return async function() {
const files = (await getFilePaths()).map(file => fs.readFile(file, 'utf8'));
for(const file of files)
console.log(await file);
};
})(mock());
console.log(`Running at ${new Date() - start}`);
await printFiles();
console.log(`Finished running at ${new Date() - start}`);
})();
for ... of ...
行得通吗?