Улучшение рекурсии Javascript

Кто-то на работе шутливо отправил электронное письмо с html файлом, предназначенным для разбивки вашего браузера, который был следующим

<html>
<script type="text/javascript">
function crash(){
  for(i=0;i<5000000001;i++){
    document.write(i);
  }
}
</script>
<body onload="crash();">
</body>
</html>

В любом случае, это не очень хорошо работает в Chrome, и возник вопрос о том, что он создал дружеское соревнование, чтобы увидеть, кто может писать javascript, чтобы сделать страницу до 5 000 000 000 как можно быстрее, не заставляя браузер перестать отвечать на запросы или сбой.

Я придумал следующий фрагмент javascript, который предназначен для использования в Chrome.

<html>
<script type="text/javascript">
function countToFiveBillion(counter, num){
  if(num < 5000000000)
  {
    num++;
    if(num % 18700 == 0){
      counter.innerHTML = num;
      setTimeout(function() {countToFiveBillion(counter, num)}, 1);
    } else {
      countToFiveBillion(counter, num);
    }
  }
}
function initiateCountDown()
{
   var counter = document.getElementById("counter");
   var num = +counter.innerHTML;
   countToFiveBillion(counter, num);
}
</script>
<body onload="initiateCountDown();">
<div id="counter">0</div>
</body>

</html>

Причина, по которой это будет выполняться только в chrome, заключается в том, что я использую вызов setTimeout, чтобы избежать создания stackoverflow в chrome. (Chrome также позволяет вам получить самый большой стек для рекурсивных вызовов из всех браузеров).

Есть ли какой-нибудь способ ускорить этот подсчет? Я думаю, что я могу увеличить количество, подсчитанное немного, прежде чем оно вызовет переполнение (где-то менее 100). Единственное условие - это отобразить как можно больше чисел, поскольку оно подсчитывается.


Улучшенный код:

<html>
<script type="text/javascript">
var counter;
var num = 0;
function countToFiveBillion(){
    if(num < 5000000000)
    {
    num++;
    if(num % 18701 == 0){
        setTimeout("countToFiveBillion()", 1);
            counter.value = num;
        } else {
        countToFiveBillion();
    }
    } else {
        counter.value = "number greater than 5 Billion";
    }
}
function initiateCountDown()
{
   counter = document.getElementById('counter');
   countToFiveBillion();
}
</script>
<body onload="initiateCountDown();">
    <input type="text" id="counter" value="0" />
</body>

</html>
  • Сделано счет и элемент globabl
  • Переключение на текстовый ввод вместо div
  • изменил пользовательский интерфейс обновления после установки обратного вызова

Ответ 1

Не используйте .innerHTML = ... для отображения номера. Согласно этот тест, установка свойства value входного элемента более эффективна.

<input type="text" id="counter" value="0" />

Вместо того, чтобы создавать новую функцию, я рекомендую использовать глобальные/локальные переменные и передавать ссылку на функцию в качестве аргумента для setTimeout или использовать setInterval в init.

  • Сменить setTimeout("countToFiveBillion()",1) для setTimeout(countToFiveBillion,0). Объяснение: "countToFiveBillion()" неэффективно; Сначала строка преобразуется в функцию и вызывается, затем следует следующий вызов функции. Предлагаемая функция работает только для вызова функции без создания новых. Он также назывался расколом секунды быстрее.
  • Поднимите лимит (я смог увеличить 18701 до 20000). Поднимая ограничение на такое округленное число, я заметил, что значение counter обновляется между каждым тайм-аутом.
  • Исправлены некоторые ошибки в реализации (заменен .innerHTML на .value на else-block).

Соответствующий код:

<input type="text" id="counter" />
<script>
var counter, num = 0;
function countToFiveBillion(){
    if(num < 5e9)
    {
        if(++num % 18701 == 0){
            setTimeout(countToFiveBillion, 0);
            counter.value = num;
        } else {
            countToFiveBillion();
        }
    } else {
        counter.value = "number greater than 5 Billion";
    }
}
function initiateCountDown(){
    counter = document.getElementById('counter');
    counter.value = num; //Init, show that the script is 
    countToFiveBillion();
}
window.onload = initiateCountDown;
</script>

Fiddle: http://jsfiddle.net/KTtae/

Ответ 2

Пример веб-рабочего, index.html

<!DOCTYPE HTML>
<html>
<head>
    <title>5 billion</title>
</head>
<body>
    <input type="text" id="counter" value="0" />
    <script type="text/javascript" charset="utf-8">
        var 
            iCounter = document.getElementById('counter')
            , counter = new Worker('worker.js');

        iCounter.value = 0;
        counter.addEventListener('message', function (e) {
            iCounter.value = e.data;
        }, false);
    </script>
</body>
</html>

worker.js:

for (var i = 0; i < 5e9; i++) {
    if (i % 18701 === 0) {
        postMessage(i);
    }
}

При необходимости подсчет может быть разбит на несколько работников.