Ответ 1
У вас есть выбор с или без WebWorkers:
Без WebWorkers
Для кода, который должен взаимодействовать с DOM или множеством других состояний в вашем приложении, вы не можете использовать webWorker, поэтому обычное решение состоит в том, чтобы разбить вашу работу на куски, выполняющие каждый кусок работы по таймеру. Разрыв между порциями с таймером позволяет ядру браузера обрабатывать другие происходящие события и не только позволяет обрабатывать ввод данных пользователем, но и позволяет рисовать экран.
Обычно вы можете позволить себе обрабатывать более одного на каждый таймер, что более эффективно и быстрее, чем одно на каждый таймер. Этот код дает потоку пользовательского интерфейса возможность обрабатывать любые ожидающие события пользовательского интерфейса между каждым чанком, что будет поддерживать его активным.
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);
Вместо того, чтобы гадать, сколько нужно разбить на чанки, можно также указать, что прошедшее время будет ориентиром для каждого чанка, и позволить ему обрабатывать как можно больше за данный промежуток времени. Это в какой-то степени автоматически гарантирует отзывчивость браузера независимо от того, насколько интенсивна загрузка процессора. Таким образом, вместо того, чтобы передавать размер куска, вы можете передать значение в миллисекундах (или просто использовать интеллектуальное значение по умолчанию):
// 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, на отдельный файл сценария, но он может выполняться до завершения, не беспокоясь о блокировке обработки других событий в браузере и не беспокоясь о подсказке "unresponsive script" это может произойти при выполнении длительного процесса в основном потоке и без блокировки обработки событий в пользовательском интерфейсе.