首页 > 解决方案 > Javascript 将一些计算结果一一追加到表行中

问题描述

我正在开发一个简单的 electron / node.js 应用程序,它在本地获取输入信息,进行一些计算并将计算结果显示在表格中

输入:

10s of rows of features info
ID | length | depth | 

计算:每个特征计算大约需要几秒钟。

输出:

Some amount of row from input, but with a results column 
ID | length | depth | Results 

我试图将结果附加到表中以进行呈现,代码如下所示

<table id = 'results-table'>
    <th>ID</th><th>length</th><th>depth</th><th>Results</th>
</table>
function getCrackTableValues(){
  document.body.style.cursor = 'wait';
  var table = document.getElementById('input-table'); //get info from input table
  for (var r = 1, n = table.rows.length; r < n; r++) { //skip table head tart from second row
      const crackID = table.rows[r].cells[0].innerHTML; //input
      var a_m = Number(table.rows[r].cells[1].innerHTML); // input
      var c_m = Number(table.rows[r].cells[2].innerHTML); //input
      var result = foo(a_m, c_m); //foo just for example, takes seconds for calculation 
      var newRow = document.getElementById('results-table').insertRow(); //output table
      newRow.innerHTML = '<td>'+ crackID+ 
                        '</td><td>' + `${a_m.toFixed(1)}` +
                        '</td><td>' + `${c_m.toFixed(1)}` +
                        '</td><td>' + `${result.toFixed(1)}` + //append results to output table
                        '</td>';
  }
  document.body.style.cursor = 'default';
}

该代码工作正常,但有几个问题。

Q1:我希望一次计算完成后,它会在输出表上显示结果,然后转到下一个条目的计算。因此用户可以看到当前结果,而其余行的计算仍在继续。

目前,在所有计算完成之前,输出表似乎不会显示。

随着每一行的计算继续进行,有什么方法可以让结果表一一增长/显示

Q2:我试图在计算过程中禁用光标document.body.style.cursor = 'wait';。并在所有计算完成后重新启用光标。

但似乎这一行是计算后执行的,它只会禁用光标并闪回。

我当前的实施是否存在任何潜在问题?我的 node.js 是 v12.16.3,在旧的 32 位 windows 7 上。

标签: javascripthtmlnode.jselectron

解决方案


这两个问题的原因是相同的:UI 将没有机会更新,直到它设法逃脱您的非常长时间运行的循环。

您需要更改foo()为异步函数。你没有显示任何细节,所以我假设它是纯粹的 CPU 计算,没有文件或网络访问点。

我想我首先将循环内容提取到另一个函数中。然后我将循环终止条件放在该新函数的顶部:

function processOneRow(r){
  const table = document.getElementById('input-table');
  if(r >= table.rows.length){ //All done
    document.body.style.cursor = 'default';
    return;
    }

  const crackID = table.rows[r].cells[0].innerHTML;
  const a_m = Number(table.rows[r].cells[1].innerHTML);
  const c_m = Number(table.rows[r].cells[2].innerHTML);

  const result = foo(a_m, c_m);

  const newRow = document.getElementById('results-table').insertRow();
  newRow.innerHTML = '<td>'+ ...;

  setTimeout(processOneRow, 0, r+1)
}

然后你可以通过在第一行调用它来开始它:

function getCrackTableValues(){
  document.body.style.cursor = 'wait';
  setTimeout(processOneRow, 0, 1)
}

使用setTimeout()0ms 延迟,给 UI 线程每次更新的时间,然后再调用processOneRow()输入表的下一行。

顺便说一句:把代码恢复到里面的光标processOneRow()有点代码味道。如果此代码在库中,我可能会使用before()/after()钩子。(然后,您还可以确保在after()抛出异常时调用该钩子。)

解决此问题的另一种方法是使用工作线程并将foo(a_m, c_m)计算移到那里。那自然是异步的。除了额外的复杂性之外,其缺点是如果foo()使用数据,则需要将其保存在该工作线程中,如果在主线程中需要相同的数据,这将变得复杂。但是,否则,对于长时间运行的进程来说,它是一个更好的解决方案。


推荐阅读