小编典典

遍历数组而不阻塞UI的最佳方法

javascript

我需要遍历一些大型数组,并将它们存储在API调用的主干集合中。在不导致循环导致接口无响应的情况下执行此操作的最佳方法是什么?

由于返回的数据太大,ajax请求的返回也会阻塞。我认为我可以将其拆分并使用setTimeout使其在较小的块中异步运行,但是有一种更简单的方法来执行此操作。

我认为网络工作者会很好,但需要更改一些保存在UI线程上的数据结构。我尝试使用它来执行ajax调用,但是当它将数据返回到UI线程时,仍然有一段时间接口不响应。

提前致谢


阅读 330

收藏
2020-04-25

共1个答案

小编典典

您可以选择是否使用webWorkers:

没有WebWorkers

对于需要与DOM或应用程序中的许多其他状态进行交互的代码,您不能使用webWorker,因此通常的解决方案是将工作分解为多个块,并在计时器上完成每个块的工作。计时器之间的块之间的中断使浏览器引擎可以处理其他正在进行的事件,不仅可以处理用户输入,还可以绘制屏幕。

通常,您可以负担每个计时器处理多个计时器的效率,这比每个计时器仅处理一个计时器效率更高,速度更快。此代码使UI线程有机会处理每个块之间的任何未决UI事件,这将使UI保持活动状态。

function processLargeArray(array) {
    // set this to whatever number of items you can process at once
    var chunk = 100;
    var index = 0;
    function doChunk() {
        var cnt = chunk;
        while (cnt-- && index < array.length) {
            // process array[index] here
            ++index;
        }
        if (index < array.length) {
            // set Timeout for async iteration
            setTimeout(doChunk, 1);
        }
    }    
    doChunk();    
}

processLargeArray(veryLargeArray);

这是该概念的一个可行示例-不是相同的功能,而是一个使用相同setTimeout()思想通过多次迭代测试概率场景的长期运行过程:


您可以将上述内容制作成更通用的版本,.forEach()像这样的调用回调函数:

// last two args are optional
function processLargeArrayAsync(array, fn, chunk, context) {
    context = context || window;
    chunk = chunk || 100;
    var index = 0;
    function doChunk() {
        var cnt = chunk;
        while (cnt-- && index < array.length) {
            // callback called with args (value, index, array)
            fn.call(context, array[index], index, array);
            ++index;
        }
        if (index < array.length) {
            // set Timeout for async iteration
            setTimeout(doChunk, 1);
        }
    }    
    doChunk();    
}

processLargeArrayAsync(veryLargeArray, myCallback, 100);

除了可以一次猜测要分割多少块,还可以让经过的时间作为每个分割块的指导,并使其在给定的时间间隔内尽可能多地进行处理。不管迭代有多密集,CPU都会自动保证浏览器的响应能力。因此,您可以传递毫秒值(或仅使用智能默认值),而不用传递块大小:

// last two args are optional
function processLargeArrayAsync(array, fn, maxTimePerChunk, context) {
    context = context || window;
    maxTimePerChunk = maxTimePerChunk || 200;
    var index = 0;

    function now() {
        return new Date().getTime();
    }

    function doChunk() {
        var startTime = now();
        while (index < array.length && (now() - startTime) <= maxTimePerChunk) {
            // callback called with args (value, index, array)
            fn.call(context, array[index], index, array);
            ++index;
        }
        if (index < array.length) {
            // set Timeout for async iteration
            setTimeout(doChunk, 1);
        }
    }    
    doChunk();    
}

processLargeArrayAsync(veryLargeArray, myCallback);

使用WebWorkers

如果循环中的代码不需要访问DOM,则可以将所有耗时的代码放入webWorker中。webWorker将独立于主浏览器Javascript运行,然后完成后,它可以通过postMessage返回任何结果。

WebWorker要求将将在WebWorker中运行的所有代码分离到一个单独的脚本文件中,但是它可以完全运行,而不必担心阻塞浏览器中其他事件的处理,也不必担心“无响应脚本”提示当在主线程上执行长时间运行的过程且不阻止UI中的事件处理时,可能会出现这种情况。

2020-04-25