development

UI를 차단하지 않고 배열을 반복하는 가장 좋은 방법

big-blog 2020. 12. 12. 12:10
반응형

UI를 차단하지 않고 배열을 반복하는 가장 좋은 방법


일부 대형 배열을 반복하고 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와 함께

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.

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.


Here's a demo of doing this "async" loop. it "delays" iteration for 1ms and within that delay, it gives the UI a chance to do something.

function asyncLoop(arr, callback) {
    (function loop(i) {

        //do stuff here

        if (i < arr.Length) {                      //the condition
            setTimeout(function() {loop(++i)}, 1); //rerun when condition is true
        } else { 
            callback();                            //callback when the loop ends
        }
    }(0));                                         //start with 0
}

asyncLoop(yourArray, function() {
    //do after loop  
})​;

//anything down here runs while the loop runs

There are alternatives like web workers and the currently proposed setImmediate which afaik, is on IE, with a prefix.


Building on @jfriend00, here is a prototype version:

if (Array.prototype.forEachAsync == null) {
    Array.prototype.forEachAsync = function forEachAsync(fn, thisArg, maxTimePerChunk, callback) {
        let that = this;
        let args = Array.from(arguments);

        let lastArg = args.pop();

        if (lastArg instanceof Function) {
            callback = lastArg;
            lastArg = args.pop();
        } else {
            callback = function() {};
        }
        if (Number(lastArg) === lastArg) {
            maxTimePerChunk = lastArg;
            lastArg = args.pop();
        } else {
            maxTimePerChunk = 200;
        }
        if (args.length === 1) {
            thisArg = lastArg;
        } else {
            thisArg = that
        }

        let index = 0;

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

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

        doChunk();
    }
}

참고URL : https://stackoverflow.com/questions/10344498/best-way-to-iterate-over-an-array-without-blocking-the-ui

반응형