当前位置: 首页 > 面试题库 >

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

阎星河
2023-03-14
问题内容

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

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

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

提前致谢


问题答案:

您可以选择是否使用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()思想通过多次迭代测试概率场景的长期运行过程: http
//jsfiddle.net/jfriend00/9hCVq/

您可以将上述内容制作成更通用的版本,.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中的事件处理时,可能会出现这种情况。



 类似资料:
  • 我正在用PHP写作。 我想做的是像Magento2中的可配置产品或Woocommerce中的可变产品。我要求用户输入产品的属性,如颜色、大小等”。 将每个属性视为一个属性类别,其中包含属性,例如“颜色”将具有“红色”、“绿色”、“蓝色”等属性。大小将具有“大”、“小”、“中”等属性。 现在,我需要做一个循环,它接受所有大小、颜色和其他选定的属性,并返回所有可能的配置。同时,要循环的属性数量没有预定

  • 哪个Java并发集合提供数组元素级锁定或数组元素的原子更新。我不想锁定整个数组。只有99%的读操作和1%的写操作。 在数组中写入时锁定可能会阻塞其他线程,这些线程甚至可能不会查看正在由阻塞线程更新的相同元素。

  • 问题内容: 目前,我正在使用类似: 我对这种方法不是很满意,因为它会创建大量数组(可以包含一本书)。 有没有更好的解决方案来迭代a的行? 问题答案: 您可以使用: 并使用方法:

  • 问题内容: 今天,当我获得一段已经使用了数百次的代码时,我很高兴地开始编码: 遍历一个Collection(这里是ArrayList) 由于某种原因,我实际上查看了Eclipse的自动完成选项,这让我感到奇怪: 在哪些情况下,以下循环比其他情况更好使用? 经典的数组索引循环: 迭代器具有next()/ next(): 我最喜欢的是因为它写起来很简单: 问题答案: 当您还需要元素的索引时,第一个很有

  • Array类实现了迭代器,可使用迭代器对数组进行遍历,如果是数字索引数组,还可以直接使用for循环进行遍历。 数字索引数组 for(int i = 0; i < array.count(); i++) { php::echo("key=%d, value=%s.\n", i, array[i].toCString()); } 迭代器 for(auto i = array.begin();

  • 问题内容: 我从书中得知,您应该为循环编写这样的代码: 因此不会每次都计算。 其他人则说编译器会对此做一些优化,因此您可以编写: 我只想知道哪种是最佳实践? 问题答案: 在使用大多数现代浏览器执行此测试之后… http://jsben.ch/dyM52 当前,最快的循环形式(我认为在语法上最明显)。 具有长度缓存的循环的标准 我想肯定的是,我为JavaScript引擎开发人员鼓掌。应该优化运行时间