如何在不暂停的情况下在 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 事件循环中使用它 ;)