Улучшение рекурсии 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);
}
}
При необходимости подсчет может быть разбит на несколько работников.