使用setTimeout绕过IE脚本警告

时间:2010-10-20 20:25:13

标签: javascript internet-explorer settimeout setinterval

我正在尝试编写一个使用Javascript执行相当复杂的计算的Web应用程序(涉及阶乘和贝塞尔函数)。当我在IE中运行脚本时,它会向我发出警告,指出脚本没有响应或需要很长时间,并询问我是否要继续运行它。我已经阅读过这个,你可以使用setTimeout或setInterval命令来实质上重置IE用来确定脚本是否长时间运行的计数器。

我尝试过这个,但没有成功。当我运行一个分析器时,似乎我的函数计算一个阶乘是大部分时间,所以我想在该函数中使用setTimeout。这是我目前的功能:

function factorial(x) {
    var buff = 1;

    for (i=x;i>=1;i--) {
        buff = buff * i;
    }

    return buff
}

我尝试用这样的代码替换代码,但它无法正常工作:

function factorial(x) {
    if (x==0) {
        factbuff=1;
    }
    else {
        factbuff = x;
        factidx = x;
        setTimeout('dofact()',50);
    }
    return factbuff
}

function dofact() {
    if (factidx > 1) {
        factidx--;
        factbuff = factbuff * factidx;
    }
}

任何人都知道我做错了什么以及如何在IE中消除脚本警告的同时正确实现setTimeout函数以计算阶乘?

1 个答案:

答案 0 :(得分:0)

这是一个更新,并解释为什么先前答案(现已删除)中的代码是错误的。

首先,让我们重申一下问题:允许析象函数在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;
    }
}

好的,那该代码有什么问题?

  1. 阶乘函数本身是递归的。这意味着该函数调用自身。每次函数调用自身时,都会在内存中获得另一个堆栈帧。上面的代码尝试做的是自己调用一百次。我不知道浏览器可以容忍多少嵌套堆栈帧,但100似乎有点高。我会分批进行10次。

  2. 上面提出的功能不是异步的。当您使用setTimeout()来绕过IE警告时,该函数需要变为异步。这意味着 - 不是像var value = func(x);那样调用它,而是需要转换代码以传递回调,async函数在得到结果时会调用它。

  3. 与上述问题有关,在建议的代码中使用setTimeout是错误的。在一个地方,代码执行此操作:

    return (function() {  // NO NO NO
        var y = x;
        setTimeout(function(y) { return y*factorial(y-1); }, 50);
    })();
    
  4. 那是做什么的?让我们分解吧。它有一个匿名功能。该函数被调用(由最后的开放式关闭)。并且主要因子函数返回该调用的值。调用anon函数的价值是什么?问题a:它没有返回值。这是未定义的。 (见What does javascript function return in the absence of a return statement?

    修复它并不像将调用值返回setTimeout()那么简单。那也是错的。 The return value of setTimeout() is an identifier that can be used to clear the timeout with 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);
    }
    

    按钮单击调用“compute”,并向其传递使用结果调用的函数的名称。使用结果调用的函数实际上是显示。这是异步调用模式。

    好的,现在计算了。

    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“长时间运行的脚本”警告。为了避免警告,我们进行异步,这意味着“computeFactorial”的返回值不是实际的阶乘。在异步模型中,我们通过“副作用”得到结果 - 来自计算函数在完成计算时调用的回调函数。