如何在不暂停的情况下在 NodeJS 中读取大文件 (Excel) UI
How to read large files (Excel) in NodeJS without pausing UI
我目前正在使用 ExcelJS 将大型 Excel 文件(10,000 多行)读入 NodeJS/Angular/Electron 应用程序。它可以很好地读取较小的文件,但读取较大的文件需要 3.9 到 5 秒,并且在此期间,CSS 根本不会更新。
我目前正在使用 async/await 来加载文件,因为我认为这会允许在等待文件加载时进行其他操作,因为我认为 I/O 节点中的操作有它们的自己的线程。我还读到 CPU 密集型任务会阻塞所有其他节点进程。
这是当前加载工作簿的代码:
async openWorkbook(filename: string) {
this.loading = true;
const workbook = new Workbook();
const promise = workbook.xlsx.readFile(this.path + '/sheets/' + filename)
.then(() => {
// use workbook
workbook.getWorksheet(1).name = workbook.getWorksheet(1).name.slice(0, 31);
const excelFile: ExcelFile = { workbook: workbook, filename: filename };
this.wbSource.next(excelFile);
});
const read = await promise;
}
此代码与回调实现的工作原理相同,它们都阻止了应用程序的 CSS。我将如何在不阻塞 UI 的情况下读取文件(或执行任何 CPU 密集型任务)?
JavaScript 是一种单线程语言。如果您的一项任务需要很长时间才能完成,则渲染等其他任务将被阻止。我无法为您编写完整的代码,但 Web Worker 似乎是您尝试做的事情的完美解决方案。 (哪些人已经在评论中建议了)
您可以参考:
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
此外,还请查看 https://github.com/GoogleChromeLabs/comlink,它是 Web Worker API 的包装器。它会让您更轻松地将现有代码迁移到 worker 上,麻烦更少。
我将使用评论中的 Thread 函数将我的解决方案留在这里。
async openWorkbook(filename: string) {
this.loading = true;
var path = this.path + '/sheets/' + filename;
const excelFile = await Thread(path, function(path, exit) {
// You'll need to import excel again since it's a new thread
import Excel from 'exceljs';
const workbook = new Excel.Workbook();
workbook.xlsx.readFile(path).then(() => {
workbook.getWorksheet(1).name = workbook.getWorksheet(1).name.slice(0, 31);
const excelFile: ExcelFile = {
workbook: workbook,
filename: filename
};
exit(excelFile);
});
});
this.wbSource.next(excelFile);
}
在这种情况下,我要做的第一件事就是根本不 "opening" 大文件来防止内存开销。
所以像这样的东西应该可以工作:
const stream = fs.createReadStream(filePath);
const workbook = new Excel.Workbook();
stream.pipe(workbook.xlsx.createInputStream());
并且由于那些 chunks 会在一小段时间内阻止循环,您甚至可以在 javascript 事件循环中使用它 ;)
我目前正在使用 ExcelJS 将大型 Excel 文件(10,000 多行)读入 NodeJS/Angular/Electron 应用程序。它可以很好地读取较小的文件,但读取较大的文件需要 3.9 到 5 秒,并且在此期间,CSS 根本不会更新。
我目前正在使用 async/await 来加载文件,因为我认为这会允许在等待文件加载时进行其他操作,因为我认为 I/O 节点中的操作有它们的自己的线程。我还读到 CPU 密集型任务会阻塞所有其他节点进程。
这是当前加载工作簿的代码:
async openWorkbook(filename: string) {
this.loading = true;
const workbook = new Workbook();
const promise = workbook.xlsx.readFile(this.path + '/sheets/' + filename)
.then(() => {
// use workbook
workbook.getWorksheet(1).name = workbook.getWorksheet(1).name.slice(0, 31);
const excelFile: ExcelFile = { workbook: workbook, filename: filename };
this.wbSource.next(excelFile);
});
const read = await promise;
}
此代码与回调实现的工作原理相同,它们都阻止了应用程序的 CSS。我将如何在不阻塞 UI 的情况下读取文件(或执行任何 CPU 密集型任务)?
JavaScript 是一种单线程语言。如果您的一项任务需要很长时间才能完成,则渲染等其他任务将被阻止。我无法为您编写完整的代码,但 Web Worker 似乎是您尝试做的事情的完美解决方案。 (哪些人已经在评论中建议了)
您可以参考:
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
此外,还请查看 https://github.com/GoogleChromeLabs/comlink,它是 Web Worker API 的包装器。它会让您更轻松地将现有代码迁移到 worker 上,麻烦更少。
我将使用评论中的 Thread 函数将我的解决方案留在这里。
async openWorkbook(filename: string) {
this.loading = true;
var path = this.path + '/sheets/' + filename;
const excelFile = await Thread(path, function(path, exit) {
// You'll need to import excel again since it's a new thread
import Excel from 'exceljs';
const workbook = new Excel.Workbook();
workbook.xlsx.readFile(path).then(() => {
workbook.getWorksheet(1).name = workbook.getWorksheet(1).name.slice(0, 31);
const excelFile: ExcelFile = {
workbook: workbook,
filename: filename
};
exit(excelFile);
});
});
this.wbSource.next(excelFile);
}
在这种情况下,我要做的第一件事就是根本不 "opening" 大文件来防止内存开销。
所以像这样的东西应该可以工作:
const stream = fs.createReadStream(filePath);
const workbook = new Excel.Workbook();
stream.pipe(workbook.xlsx.createInputStream());
并且由于那些 chunks 会在一小段时间内阻止循环,您甚至可以在 javascript 事件循环中使用它 ;)