Это обновление и объяснение, почему код в предыдущем ответе (теперь удаленный) неправильный.
Сначала давайте повторим проблему: разрешаем выполнение факториальной функции в IE, не вызывая "длительный скрипт "предупреждение.
Это код, который был предложен ранее:
BROKEN. DO NOT USE.
var executions = 0;
function factorial(x) {
executions++;
if (x > 1) {
if (executions % 100 === 0) {
return (function() { // NO NO NO
var y = x;
setTimeout(function(y) { return y*factorial(y-1); }, 50);
})();
} else {
return x*factorial(x-1);
}
} else {
return 1;
}
}
Хорошо, так что же с этим кодом?
Факториальная функция сама является рекурсивной.Это означает, что функция вызывает себя.Каждый раз, когда функция вызывает себя, вы получаете другой кадр стека в памяти.То, что код выше пытается сделать, это назвать себя сто раз.Я не знаю, сколько вложенных стековых фреймов может выдержать браузер, но 100 кажется немного высоким.Я бы сделал это партиями по 10.
Предложенная выше функция не является асинхронной.Когда вы используете setTimeout (), чтобы обойти предупреждение IE, функция должна стать асинхронной.Это означает - вместо того, чтобы вызывать его как var value = func(x);
, вам нужно будет преобразовать свой код для передачи обратного вызова, который асинхронная функция вызывает, когда у нее есть результат.
, связанный сПроблема выше, использование setTimeout в предложенном коде неправильно.В одном месте код делает это:
return (function() { // NO NO NO
var y = x;
setTimeout(function(y) { return y*factorial(y-1); }, 50);
})();
Что это делает?Давайте разберемся с этим.У него есть анонимная функция.Эта функция вызывается (паренем open-close в конце).И значение этого вызова возвращается основной факториальной функцией.Какова ценность вызова анон функции?Проблема а: не возвращает значение.Это не определено.(См. Что возвращает функция javascript при отсутствии оператора return? )
Исправить это не так просто, как вернуть значение вызова в setTimeout()
.Это тоже неправильно. Возвращаемое значение setTimeout()
- это идентификатор, который можно использовать для очистки тайм-аута с помощью clearTimeout()
. Это определенно НЕ значение, доставляемое тем, что вызывает setTimeout.
ок, как это исправить?Во-первых, осознайте, что факториальная функция будет асинхронной, поэтому получение и отображение результата будет выглядеть так:
function displayFacResult(result) {
var t2 = document.getElementById('t2');
t2.value = result;
}
function b1_Click() {
var t1 = document.getElementById('t1'),
value = parseInt(t1.value, 10);
computeFactorialAsynchronously(value, displayFacResult);
}
Нажатие кнопки вызывает «вычислить» и передает ей имя функции, которая вызываетсярезультат.Функция, которая вызывается с результатом, фактически выполняет отображение.Это шаблон асинхронного вызова.
хорошо, теперь вычисление.
function computeFactorialAsynchronously(firstX, callback) {
var batchSize = 3, limit=0, result = 1;
var doOneFactorialStep = function(x, steps) {
if (steps) { limit = x - steps; }
if (x==1) { callback(result); }
if (x>limit) {
result *= x;
doOneFactorialStep(x-1);
}
else {
setTimeout(function () {
doOneFactorialStep(x, batchSize);
}, 1);
}
};
doOneFactorialStep(firstX, batchSize);
// the actual return value of the computation
// always comes in the callback.
return null;
}
Он вычисляет факториалы по «чанку», каждый чанк включает в себя N умножений и представлен переменной «шаги» в приведенном выше.Значение N (шагов) определяет уровень рекурсии.100, вероятно, слишком велик.3, вероятно, слишком мал для хорошей производительности, но это иллюстрирует асинхронность.
Внутри функции computeFactorialAsynchronously есть вспомогательная функция, которая вычисляет один фрагмент, а затем вызывает setTimeout для вычисления следующего фрагмента.Есть некоторая простая арифметика, чтобы управлять, когда прекратить вычислять текущий кусок.
рабочий пример: http://jsbin.com/episip
В некотором смысле переход к асинхронной модели уводит вас от чисто функциональной метафоры, где результат вычисления является результатом функции.Мы можем сделать это в javascript, но в нем появляется предупреждение IE "long running script".Чтобы избежать предупреждения, мы используем асинхронный режим, что означает, что возвращаемое значение «computeFactorial» не является фактическим факториалом.В асинхронной модели мы получаем результат через «побочный эффект» - от функции обратного вызова, которую функция вычисления вызывает, когда она завершает вычисления.