3 000 000 000 циклов итераций ведут себя странно - программирование
Подтвердить что ты не робот

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.

4b9b3361

Ответ 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!';
}