为什么javascript函数在firefox中有效但在Chrome或IE上无效

时间:2013-07-16 15:23:20

标签: javascript jquery internet-explorer google-chrome cross-browser

我创建了一个启动活动指示器的方法和一个不同的方法来停止给定指标。然后在执行耗时的任务时,我先调用start方法,然后调用stop方法。呼叫示例如下所示:

// Show loading
viewControllerShowAjax();

// Tasks that take time including large calculations and ajax requests   
app.timeConsumingFunction();

// Hide loading
viewControllerHideAjax();

正在使用firefox ,但无法在IE或Chrome上运行。有没有解决这个问题?

修改 以下是我正在使用的功能:

function viewControllerInit() {
    // Other initializations
    ...

    // Initializing the activity indicator; the activity indicator is simply a jqueryui modal view with a spinjs spinner in it.
    $(this.ajaxModalSelector).dialog({
        resizable   : false,
    dialogClass : "no-close ajax",
    autoOpen    : false,
    height      : 500,
    width       : 500,
    modal       : true,
    open: function( event, ui ) {
        $(this.ajaxModalSelector).dialog("open");
            var opts = {
              lines: 12,
              length: 0,
              width: 20,
              radius: 40,
              color: '#000',
              speed: 1.3,
              trail: 50,
              shadow: false,
            };
            this.spinner = new Spinner(opts).spin(document.getElementById("ajax-modal"));
        },
        beforeClose: function( event, ui ) {
            this.spinner.stop();
        }
    });
}
// Opens the jquery ui modal view
function viewControllerShowAjax() {
    $(this.ajaxModalSelector).dialog("open");
}
// Closes the jquery ui modal view
function viewControllerHideAjax() {
    $(this.ajaxModalSelector).dialog("close");
}

修改:有关此问题的详细信息;我发现如果没有为活动指示器调用hide函数,那么它将在耗时的任务完成后出现。这也是IE和Chrome中的行为,但不是firefox。

1 个答案:

答案 0 :(得分:9)

可能原因

这不起作用的原因是JavaScript是单线程的。

耗时的功能app.timeConsumingFunction()会阻止用户界面在Chrome和IE中进行更新。

它在Firefox中工作的原因很简单,因为FF有一个不同的实现,允许UI事件在中间执行,可以这么说(Canary和我相信Chrome也有一个允许执行UI更新的实验模式在一个单独的线程上。)

浏览器通常会提示不同的事件,例如绘画事件。然后遍历此队列并逐个事件执行。

但是,如果队列条目例如为耗时的函数执行上下文,则队列将没有“喘息空间”来继续该队列中的其他事件。

这将是执行绘制事件的其他人,否则会绘制微调器。当它被阻塞时,在耗时的函数完成之前,不会出现微调器。

使用setTimeout

的可能解决方案

解决这个问题的方法是使用setTimeoutWeb Worker来分离执行(参见下面的Web Worker示例)。

你可以试试看它是否能改进当前的代码(我怀疑但是值得一试,因为它很简单) - 因为帖子中没有足够的代码来实际测试它,考虑它的理论。请尝试替换此行:

app.timeConsumingFunction();

setTimeout(app.timeConsumingFunction, 0); //or use 11 if no difference

如果这还不够,您将需要进行类似的设置,但需要使用函数的内部(您没有显示),如果可能的话,将其划分为处理不同操作阶段的部分。骨架示例可能是:

app.timeConsumingFunction = function() {

    var hereIsAvailableToAllParts;

    function part1() {
        // step 1 in time-consuming operation
        setTimout(part2, 0);  //start next part
    }

    function part2() {
        // step 2 in time-consuming operation
        setTimout(part3, 0);
    }

    function part3() {
        // step 3 in time-consuming operation
        ...done or next...
    }
    setTimeout(part1, 0);
}

setTimeout的0参数告诉浏览器在第一次可能的情况下对此事件进行排队。这可能并不总是有效,但您可以使用大约11毫秒的间隔,而该间隔接近最小可用队列片。

通过拆分这样的操作,您将允许浏览器执行例如之间的绘制事件,因此能够看到微调器。

使用Web Workers的可能解决方案

在不知道您的代码的情况下,我不能说Web Workers在这种情况下是可用的,因为对于那些没有DOM访问,没有存储访问等的限制存在某些限制。support for Web Workers非常好,我建议这样做尽可能使用您的代码(参考限制)。

但如果你做纯计算,可以考虑将Web Workers作为一种选择:
https://developer.mozilla.org/en-US/docs/Web/Guide/Performance/Using_web_workers

一个愚蠢的例子:

在主脚本中,您可以像这样分配Web Worker:

var ww = new Worker(urlToWebWorkerScriptHere);

Web Workers通过消息进行通信,因此在“我们”方面,我们会收听message事件:

ww.onmessage = function (e) {

    var msg = e.data;

    /// message returned can be anything you like (see next section)
    if (msg === 'Done!') viewControllerHideAjax();
};

Web Worker使用一个单独的脚本,该脚本作为单独的JS文件加载,或者您可以使用一个小技巧将其与HTML代码内联(如下面的小提琴所示)。

脚本在隔离的上下文中执行,是进行所有计算的地方。完成后,它表示我们的主要脚本:

onmessage = function(e) {

    /// you can send any commands you want and call them what you want
    /// for example, I have called the command start

    if (e.data === 'start') {

        /// the time consuming stuff..

        /// when finished, send 'Done!' to main script
        postMessage('Done!');
    }
};

要开始计算,你会这样做:

// Show loading
viewControllerShowAjax();

// Tasks that take time including large calculations and ajax requests   
ww.postMessage('start');

(当从工人返回'完成!'消息时调用hide。)

您当然可以发送和返回许多消息,例如返回部分结果并触发计算(或操作)的下一部分。

我在下面的小提琴中做了一个简单的演示 - 请注意,即使小提琴中的循环正在进行繁忙循环/“繁重工作”,您也可以单击右键(在示例中您还可以看到如何内联Web Worker脚本而不是单独加载它:

WEB WORKER DEMO

但是,再一次,如果没有看到你的工作代码我就无法判断是否可以使用Web Workers。

无论如何,这应该给出一些如何解决这个问题的建议。