【问题标题】:Javascript append some calculation results into table rows one by oneJavascript 将一些计算结果一一追加到表行中
【发布时间】:2020-12-04 10:49:50
【问题描述】:

我正在开发一个简单的 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 上。

【问题讨论】:

    标签: javascript html node.js electron


    【解决方案1】:

    这两个问题的原因是相同的:用户界面只有在设法摆脱您长时间运行的循环后才会有机会更新。

    您需要将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() 的使用有 0 毫秒的延迟,给 UI 线程每次更新的时间,然后它会在输入表的下一行调用 processOneRow()

    旁白:将用于恢复光标的代码放在processOneRow() 中有点代码味道。如果这段代码在库中,我可能会使用before()/after() 钩子。 (然后,您还可以确保在抛出异常时调用 after() 钩子。)

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

    【讨论】:

    • 感谢达伦。只是想知道是否总是只有在其他运行循环完成后才能更新 UI?即使 ui 命令出现在脚本中的其他命令之前。
    • 是的,总是如此,请参阅developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop 的“运行至完成”部分(搜索有关 JavaScript 事件循环的文章是很好的背景阅读。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-11
    • 1970-01-01
    • 2014-11-28
    • 2014-02-14
    • 2018-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多