javascript - 使用 setTimeout 绕过 IE 脚本警告

标签 javascript internet-explorer settimeout setinterval

我正在尝试编写一个网络应用程序,该应用程序使用 Javascript 执行相当复杂的计算(涉及阶乘和贝塞尔函数)。当我在 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;
    }
}

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

最佳答案

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

首先,让我们重述一下问题:允许阶乘函数在 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); 那样调用它,您需要转换代码以传递回调,异步函数在获得结果时调用回调。

  3. 与上述问题相关,在建议的代码中使用 setTimeout 是错误的。在一个地方,代码是这样做的:

    return (function() {  // NO NO NO
        var y = x;
        setTimeout(function(y) { return y*factorial(y-1); }, 50);
    })();
    

那有什么作用?让我们分解一下。它有一个匿名函数。该函数被调用(由末尾的开闭括号)。该调用的值由主阶乘函数返回。调用 anon 函数的值(value)是什么?问题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;
}

它按“ block ”计算阶乘,每个 block 涉及N次乘法,并由上面的变量“steps”表示。 N(步数)的值决定了递归的级别。 100 可能太大了。 3 对于良好的性能来说可能太小了,但它说明了异步性。

在 computeFactorialAsynchronously 函数中,有一个辅助函数计算一个 block ,然后调用 setTimeout 来计算下一个 block 。有一些简单的算法可以管理何时停止计算当前 block 。

工作示例:http://jsbin.com/episip

从某种意义上说,迁移到异步模型会让您远离纯函数隐喻,在这种隐喻中,计算结果就是函数的结果。我们可以在 javascript 中做到这一点,但它会遇到 IE“长时间运行的脚本”警告。为了避免警告,我们采用异步方式,这意味着“computeFactorial”的返回值不是实际的阶乘。在异步模型中,我们通过“副作用”获得结果——来自计算函数在完成计算时调用的回调函数。

关于javascript - 使用 setTimeout 绕过 IE 脚本警告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3981990/

相关文章:

internet-explorer - 为什么我的绿色按钮在 IE 中显示为白色?

php - 使用包含 500 多行的表时 IE 变慢

javascript - 设置浏览器关闭超时

javascript - 增加 Javascript 中的 setTimeout 间隔

javascript - 没有 jQuery 的淡入淡出

javascript - 如何使用 native react 在另一个屏幕上发送并显示文本输入的值?

JavaScript 对象在 IE 中不起作用

javascript - 倒数计时器脚本中跳过了很多秒

javascript - 如何确定 JavaScript 中 HTML 元素的类型?

javascript - png 文件上的颜色蒙版