在不阻塞 UI 的情况下迭代数组的最佳方法 [英] Best way to iterate over an array without blocking the UI

查看:51
本文介绍了在不阻塞 UI 的情况下迭代数组的最佳方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am needing to iterate over some large arrays and store them in backbone collections from an API call. What is the best way to do this without making the loop cause the interface to become unresponsive?

ajax 请求的返回也阻塞,因为返回的数据如此之大.我认为我可以将其拆分并使用 setTimeout 使其以较小的块异步运行,但有没有更简单的方法来做到这一点.

The return of the ajax request also blocks as the data returned is so large. I figure that I could split it up and use setTimeout to make it run asynchronously in smaller chunks but is there an easier way to do this.

我认为 Web Worker 会很好,但它需要更改保存在 UI 线程上的一些数据结构.我曾尝试使用它来执行 ajax 调用,但是当它向 UI 线程返回数据时,仍有一段时间界面没有响应.

I thought a web worker would be good but it needs to alter some data structures saved on the UI thread. I have tried using this to do the ajax call but when it returns the data to the UI thread there is still a time when the interface is unresponsive.

提前致谢

推荐答案

您可以选择是否使用 webWorkers:

You have a choice of with or without webWorkers:

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

For code that needs to interact with the DOM or with lots of other state in your app, you can't use a webWorker so the usual solution is to break your work into chunks do each chunk of work on a timer. The break between chunks with the timer allows the browser engine to process other events that are going on and will not only allow user input to get processed, but also allow the screen to draw.

通常,您可以在每个计时器上处理多个,这比每个计时器只处理一个更有效和更快.此代码使 UI 线程有机会处理每个块之间的任何挂起的 UI 事件,这将使 UI 保持活动状态.

Usually, you can afford to process more than one on each timer which is both more efficient and faster than only doing one per timer. This code gives the UI thread a chance to process any pending UI events between each chunk which will keep the UI active.

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/

Here's a working example of the concept - not this same function, but a different long running process that uses the same setTimeout() idea to test out a probability scenario with a lot of iterations: http://jsfiddle.net/jfriend00/9hCVq/

你可以把上面的代码变成一个更通用的版本,它调用像 .forEach() 这样的回调函数,就像这样:

You can make the above into a more generic version that calls a callback function like .forEach() does like this:

// 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 密集程度如何,这都会在一定程度上自动保证浏览器响应能力.因此,您可以传入一个毫秒值(或仅使用智能默认值),而不是传入块大小:


Rather than guessing how many to chunk at once, it's also possible to let elapsed time be the guide for each chunk and to let it process as many as it can in a given time interval. This somewhat automatically guarantees browser responsiveness regardless of how CPU intensive the iteration is. So, rather than passing in a chunk size, you can pass in a millisecond value (or just use an intelligent default):

// 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);

使用 WebWorker

如果你的循环中的代码不需要访问DOM,那么可以将所有耗时的代码放到一个webWorker中.webWorker 将独立于主浏览器 Javascript 运行,然后当它完成时,它可以用 postMessage 返回任何结果.

With WebWorkers

If the code in your loop does not need to access the DOM, then it is possible to put all the time consuming code into a webWorker. The webWorker will run independently from the main browser Javascript and then when its done, it can communicate back any results with a postMessage.

一个 webWorker 需要将所有将在 webWorker 中运行的代码分离到一个单独的脚本文件中,但它可以运行到完成而不必担心阻止浏览器中其他事件的处理,也不必担心无响应"script"提示,当在主线程上执行长时间运行的进程并且不阻塞 UI 中的事件处理时可能会出现.

A webWorker requires separating out all the code that will run in the webWorker into a separate script file, but it can run to completion without any worry about blocking the processing of other events in the browser and without the worry about the "unresponsive script" prompt that may come up when doing a long running process on the main thread and without blocking event processing in the UI.

这篇关于在不阻塞 UI 的情况下迭代数组的最佳方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆