programing

UI를 차단하지 않고 어레이를 반복하는 가장 좋은 방법

magicmemo 2023. 3. 2. 22:09
반응형

UI를 차단하지 않고 어레이를 반복하는 가장 좋은 방법

몇 개의 큰 어레이를 반복하여 API 호출의 백본 컬렉션에 저장해야 합니다.루프로 인해 인터페이스가 응답하지 않게 되지 않는 최선의 방법은 무엇입니까?

반환되는 데이터가 너무 크기 때문에 ajax 요청의 반환도 차단됩니다.분할하여 setTimeout을 사용하여 더 작은 청크로 비동기적으로 실행할 수 있지만, 더 쉬운 방법이 있을까요?

웹워커가 좋을 것 같았는데 UI 스레드에 저장된 데이터 구조를 변경해야 합니다.이를 사용하여 Ajax 호출을 시도했지만 UI 스레드로 데이터를 반환할 때 인터페이스가 응답하지 않을 수 있습니다.

잘 부탁드립니다

webWorker의 유무에 관계없이 선택할 수 있습니다.

웹 워커 없음

DOM 또는 앱의 다른 많은 상태와 상호 작용해야 하는 코드의 경우 webWorker를 사용할 수 없기 때문에 일반적으로는 작업을 청크로 분할하여 타이머에 따라 작업합니다.타이머에 의한 청크간의 중단에 의해 브라우저 엔진은 진행 중인 다른 이벤트를 처리할 수 있게 되어 사용자 입력을 처리할 수 있을 뿐만 아니라 화면도 그릴 수 있게 됩니다.

통상, 타이머 마다 복수의 처리를 실시할 수 있기 때문에, 타이머 마다 1 개만 처리하는 것보다 효율적이면서 고속입니다.이 코드를 사용하면 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);

웹 워커 사용

루프내의 코드가 DOM 에 액세스 할 필요가 없는 경우는, 시간이 걸리는 모든 코드를 webWorker 에 넣을 수 있습니다.webWorker는 메인 브라우저 Javascript로부터 독립적으로 실행되며, 실행이 완료되면 postMessage로 결과를 회신할 수 있습니다.

webWorker는 webWorker에서 실행되는 모든 코드를 별도의 스크립트파일로 분리해야 하지만 브라우저 내의 다른 이벤트 처리를 차단할 염려가 없으며 메인 스레드에서 장시간 실행 중인 프로세스를 실행할 때 발생할 수 있는 "응답하지 않는 스크립트" 프롬프트에 대한 걱정도 없이 실행할 수 있습니다.d는 UI에서 이벤트 처리를 차단하지 않습니다.

"비동기" 루프를 실행하는 데모를 보여드리겠습니다.1ms 동안 반복을 "지연"하고 그 지연 시간 내에 UI가 무언가를 수행할 수 있는 기회를 제공합니다.

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

웹워커현재 제안된 setImediate는 IE에 프리픽스가 붙어 있습니다.

@jfriend00을 기반으로 한 시제품 버전은 다음과 같습니다.

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();
    }
}

정말 감사합니다.

몇 가지 기능을 추가하기 위해 코드를 업데이트했습니다.

아래 코드를 사용하면 어레이의 함수(어레이의 반복) 또는 맵의 함수(맵의 반복) 중 하나를 사용할 수 있습니다.

또한 청크가 완료되었을 때 호출되는 함수 파라미터(메시지 로딩이 필요한 경우 도움)와 루프 처리 종료 시 호출되는 함수 파라미터(비동기 조작 완료 후 다음 단계를 수행하기 위해 필요)가 있습니다.

//Iterate Array Asynchronously
//fn = the function to call while iterating over the array (for loop function call)
//chunkEndFn (optional, use undefined if not using) = the function to call when the chunk ends, used to update a loading message
//endFn (optional, use undefined if not using) = called at the end of the async execution
//last two args are optional
function iterateArrayAsync(array, fn, chunkEndFn, endFn, 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((now() - startTime) > maxTimePerChunk && chunkEndFn !== undefined){
            //callback called with args (index, length)
            chunkEndFn.call(context,index,array.length);
        }
        if (index < array.length) {
            // set Timeout for async iteration
            setTimeout(doChunk, 1);
        }
        else if(endFn !== undefined){
            endFn.call(context);
        }
    }    
    doChunk();    
}

//Usage
iterateArrayAsync(ourArray,function(value, index, array){
    //runs each iteration of the loop
},
function(index,length){
    //runs after every chunk completes, this is optional, use undefined if not using this
},
function(){
    //runs after completing the loop, this is optional, use undefined if not using this

});

//Iterate Map Asynchronously
//fn = the function to call while iterating over the map (for loop function call)
//chunkEndFn (optional, use undefined if not using) = the function to call when the chunk ends, used to update a loading message
//endFn (optional, use undefined if not using) = called at the end of the async execution
//last two args are optional
function iterateMapAsync(map, fn, chunkEndFn, endFn, maxTimePerChunk, context) {
    var array = Array.from(map.keys());
    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, key, map)
            fn.call(context,map.get(array[index]), array[index], map);
            ++index;
        }
        if((now() - startTime) > maxTimePerChunk && chunkEndFn !== undefined){
            //callback called with args (index, length)
            chunkEndFn.call(context,index,array.length);
        }
        if (index < array.length) {
            // set Timeout for async iteration
            setTimeout(doChunk, 1);
        }
        else if(endFn !== undefined){
            endFn.call(context);
        }
    }    
    doChunk();
}

//Usage
iterateMapAsync(ourMap,function(value, key, map){
    //runs each iteration of the loop
},
function(index,length){
    //runs after every chunk completes, this is optional, use undefined if not using this
},
function(){
    //runs after completing the loop, this is optional, use undefined if not using this

});

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

반응형