Могу ли я использовать setTimeout для создания дешевого бесконечного цикла? - PullRequest
4 голосов
/ 11 августа 2011
var recurse = function(steps, data, delay) {
    if(steps == 0) {
        console.log(data.length)
    } else {
        setTimeout(function(){
            recurse(steps - 1, data, delay);
        }, delay);
    }
};

var myData = "abc";
recurse(8000, myData, 1);

Что меня беспокоит в этом коде, так это то, что я передаю строку 8000 раз. Приводит ли это к какой-либо проблеме с памятью?

Кроме того, если я запускаю этот код с node.js, он печатается немедленно, а это не то, чего я ожидал.

Ответы [ 4 ]

4 голосов
/ 11 августа 2011

Если вы беспокоитесь о том, что строка копируется 8000 раз, не беспокойтесь, есть только одна копия строки;то, что передается, является ссылкой.

Большой вопрос - сохраняется ли объект, созданный при вызове функции (называемой «объектом привязки переменной» «контекста выполнения»), потому что вы создаетезамыкание, которое имеет ссылку на объект переменной для контекста и, таким образом, сохраняет его в памяти до тех пор, пока на замыкание все еще есть ссылка.

И ответ: да, но только до срабатывания таймерапотому что, когда он ничего не делает, он больше не ссылается на замыкание, и поэтому сборщик мусора может вернуть их обоих.Так что у вас не будет 8 000 выдающихся, только один или два.Конечно, когда и как работает GC, зависит от реализации.

Любопытно, что только сегодня сегодня у нас был другой вопрос на очень похожую тему;см. мой ответ там же.

2 голосов
/ 11 августа 2011

1 - 1 миллисекунда. Это также может быть цикл for. 1 секунда - 1000. Недавно я написал что-то похожее, проверяя ход процесса на внутреннем сервере и установив задержку 500. Старые браузеры не увидели бы реальной разницы между 1 и 15 мс, если я правильно помню. Я думаю, что V8 может на самом деле обрабатывать быстрее, чем это.

Я не думаю, что сборка мусора будет происходить с любой из функций до тех пор, пока не будет завершена последняя итерация, но эти новые поколения JS JIT-компиляторов намного умнее, чем те, о которых я знаю больше, так что возможно, они увидят что после истечения времени ожидания ничего не происходит и извлекает эти параметры из памяти.

Несмотря на это, даже если память зарезервирована для каждого экземпляра этих параметров, потребуется более 8000 итераций, чтобы вызвать проблему.

Одним из способов защиты от потенциальных проблем с более интенсивными параметрами памяти является передача объекта с нужными параметрами. Тогда я верю, что params будет просто ссылкой на заданное место в памяти.

Так что-то вроде:

var recurseParams ={ steps:8000, data:"abc", delay:100 }  //outside of the function

//define the function

recurse(recurseParams);

//Then inside the function reference like this:

recurseParams.steps--
2 голосов
/ 11 августа 2011

Обычно Javascript не поддерживает оптимизацию хвостового вызова , поэтому написание рекурсивного кода обычно создает риск переполнения стека. Если вы используете setTimeout, как это, он эффективно сбрасывает стек вызовов, поэтому переполнение стека больше не проблема.

Однако производительность будет проблемой, поскольку каждый вызов setTimeout обычно занимает достаточно много времени (около 10 мс), даже если вы установите delay в 0.

2 голосов
/ 11 августа 2011

Печатается немедленно, потому что программа выполняется "немедленно".На моей машине с Intel i5 вся операция занимает 0,07 с, в соответствии с time node test.js.

Для проблем с памятью, и если это «дешевый бесконечный цикл», вам просто нужно экспериментировать и измерять.

Если вы хотите создать асинхронный цикл в узле, вы можете использовать process.nextTick.Это будет быстрее, чем setTimeout(func, 1).

...