3 000 000 000 циклов итераций ведут себя странно
Пытаясь ответить на этот вопрос, я встретил странное поведение (это не одно и то же: его из-за слишком немногих итераций, мое слишком много)
HTML:
<button id="go">it will be legend...</button>
<div id="output"></div>
JS:
var output = document.getElementById('output');
document.getElementById('go').onclick = function() {
output.textContent += 'wait for it...';
for (var i=0; i<3000000000; i++) {
var unused = i; // don't really care
}
output.textContent += ' dary!';
};
Цикл занимает несколько секунд, чтобы выполнить его из-за трехмиллиардных итераций.
После нажатия кнопки, что я ожидал:
-
wait for it...
появляется
- процесс немного зависает из-за цикла
-
dary!
появляется
Что на самом деле произошло:
- процесс немного зависает из-за цикла
-
wait for it... dary!
отображается вместе
Любая идея, почему такое поведение?
Проверьте сами: fiddle.
Ответы
Ответ 1
Причина в том, что функция в целом выполняет синхронно. К тому времени, когда вы установите выход на wait for it...
, он войдет в длинный цикл и опрокинет поток. Если вы завершите остаток в timeout
, первый текст появится как обычно.
var output = document.getElementById('output');
document.getElementById('go').onclick = function() {
output.textContent += 'wait for it...';
window.setTimeout(function() {
for (var i=0; i<3000000000; i++) {
var unused = i; // don't really care
}
output.textContent += ' dary!';
}, 0);
};
Обратите внимание, что при обработке он все равно заморозит интерфейс.
Изменить: Использование 0
, поскольку значение задержки в Chrome работает, но оно не находится в последних версиях Firefox и IE 10. Изменение значения на 10
работает в обоих случаях.
Ответ 2
Javascript в значительной степени однопоточный. Если вы используете код, страница не реагирует и не будет обновляться до завершения вашего кода. (Обратите внимание, что это специфичная реализация, но так делают все браузеры сегодня.)
Ответ 3
Темный Сокол и Саймон Беланджер объяснили причину; этот пост обсуждает другое решение. Однако это решение, безусловно, НЕ подходит для 3-миллиардного цикла итерации, поскольку он слишком медленный по сравнению.
В соответствии с этот SO-сообщение пользователя Cocco, использование setTimeout
менее оптимально, чем requestAnimationFrame
для этой цели. Итак, вот как использовать requestAnimationFrame:
Пример jsFiddle
$(document).ready(function() {
var W = window,
D = W.document,
i = 0,
x = 0,
output = D.getElementById('output');
function b() {
if (x == 0) {
output.textContent = 'wait for it...';
x++;
}
i++;
if (i < 300) {
//if (i > 20) output.textContent = i;
requestAnimationFrame(b);
} else {
D.body.style.cursor = 'default';
output.textContent += ' dary!';
}
}
function start() {
console.log(D)
D.body.style.cursor = 'wait';
b();
}
D.getElementById('go').onclick = start;
}); //END $(document).ready()
Примечание. Чтобы показать согласие/оценку, пожалуйста, воздержитесь Cocco answer в сообщении выше, а не в этом. Спасибо.
Ответ 4
Ваш код выполняется, как вы ожидаете. Проблема заключается в том, что браузер не отобразит ваше изменение в документе до тех пор, пока не будет выполнен javascript. Тайм-аут устраняет эту проблему, разбивая выполнение кода на два отдельных события. Следующий код покажет, что вы ожидали.
var output = document.getElementById('output');
document.getElementById('go').onclick = function() {
console.log('wait for it...';)
for (var i=0; i<3000000000; i++) {
var unused = i; // don't really care
}
console.log(' dary!');
};
Вам также нужно быть осторожным при использовании решения тайм-аута, поскольку выполнение больше не является синхронным.
output = document.getElementById('output');
document.getElementById('go').onclick = function() {
output.textContent += 'wait for it...';
window.setTimeout(function() {
for (var i = 0; i < 3000000000; i++) {
var unused = i;
// don't really care
}
output.textContent += ' dary!';
}, 0);
output.textContent += ' epic';
};
Если вы запустите эту версию, вы заметите, что "epic" до "dary".
Ответ 5
Единственное объяснение, которое я вижу, это то, что браузер обновляет представление после выполнения javascript? В качестве доказательства это работает так, как ожидалось:
var output = document.getElementById('output');
document.getElementById('go').onclick = function () {
output.textContent += 'wait for it...';
window.setTimeout(count, 100);
};
function count() {
for (var i = 0; i < 3000000000; i++) {
var unused = i; // don't really care
}
output.textContent += ' dary!';
}