JavaScript Array.sort:显示进度指示器

z80*_*rew 0 javascript

sort我正在对一个大数组进行排序,并希望在运行时更新我的​​网络应用程序中的 UI 。在我的自定义比较函数中,我每进行 10,000 次(左右)比较就会更新一次 HTML 内容,但浏览器不会反映这些更改。

large_array.sort( (a,b) => {
    ++comparisons
    if( comparisons % 10000 == 0 ) {
        // this works:
        console.log(comparisons)
        // this doesn't:
        document.querySelector('#sort-progress').textContent = comparisons
    }
    return custom_sort(a,b)
})
Run Code Online (Sandbox Code Playgroud)

我发现使用网络工作者应该是“正确”的解决方案,但这意味着完全重新设计应用程序。所以我希望找到一些解决方法。

T.J*_*der 6

您当前的方法不起作用,因为 JavaScript 代码在主 UI 线程上运行,并将其绑定直到sort完成,因此浏览器无法更新页面显示,直到sort结束并且 JavaScript 代码完成运行,从而允许事件循环完成。

我想到了几个选项:

  1. 使用网络工作者(如您所示)。

  2. 对数组进行分块排序,让浏览器在触发对下一个块进行排序之前重新绘制setTimeout(..., 0)。不要过于频繁地让步,否则你最终会花很长时间来对结果进行排序,但偶尔一次应该是好的。

我发现使用网络工作者应该是“正确”的解决方案,但这意味着完全重新设计应用程序

这在很大程度上取决于您要排序的内容。如果您正在对可传输的或可以使用结构化克隆算法序列化的任何内容进行排序,则无需为此完全重新设计应用程序。你需要:

  1. postMessage将数组发送给工作人员(如果可能的话进行转移)。

  2. 让工作人员发送您用于更新 UI 的进度更新。

  3. 让工作人员将postMessage排序后的数组返回到您的代码中。

但如果数组中的内容不能这样处理,确实会涉及更多的工作。例如,您可能:

  1. 构建一个新数组,仅包含排序所需的信息以及数组中每个对象的原始索引。

  2. postMessage将该数组发送给网络工作者。

  3. 让工作人员发送您用于更新 UI 的进度更新。

  4. 让工作人员将postMessage排序后的数组返回到您的代码中。

  5. 循环接收到的数组,要么将元素放在它们应该去的地方(例如,进行交换操作),要么按照元素应该去的顺序构建一个新数组。

尽管如此,将 Timsort 实现为async具有周期性暂停的函数可能会更简单。