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
'programing' 카테고리의 다른 글
json 파일 설명 추가 (0) | 2023.03.02 |
---|---|
html5의 소스 "src" 속성을 변경해도 angularjs에는 적용되지 않습니다. (0) | 2023.03.02 |
JQ: 여러 조건 선택 (0) | 2023.03.02 |
Angular에서 ng-repeat을 사용하여 맵엔트리를 반복하는 방법JS (0) | 2023.03.02 |
AngularJS 인증 + RESTful API (0) | 2023.03.02 |