【问题标题】:Why does a javascript function work in firefox but not work on Chrome or IE为什么 javascript 函数在 Firefox 中有效,但在 Chrome 或 IE 中无效
【发布时间】:2013-07-16 15:23:20
【问题描述】:

我创建了一个启动活动指示器的方法和一个停止给定指示器的不同方法。然后在做一些耗时的任务时,我会先调用 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");
}

编辑:有关问题的更多信息;我发现如果没有为活动指示器调用隐藏函数,那么它会在耗时任务完成后出现。同样,这是 IE 和 Chrome 中的行为,但不是 firefox。

【问题讨论】:

  • 您需要提供有关“活动指标”的更多信息。它如何具体表明活动?
  • 这完全取决于这些函数的作用。
  • 您能否发布一些这些函数的代码,以便我们更好地帮助您?或者更有帮助的是有一个 jsfiddle 示例
  • There is no way this works in Firefox either。也许您正在测试缓存代码或其他东西? async jsfiddle
  • @Esailija 它确实适用于 firefox,但我不知道为什么会这样,为什么它在 chrome 或 ie 上都不起作用?我应该以其他方式执行此操作吗?

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


【解决方案1】:

可能的原因

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

耗时函数app.timeConsumingFunction()会阻止UI在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');

(当工作人员返回“完成!”消息时调用隐藏)。

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

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

WEB WORKER DEMO

但同样,如果没有看到您的工作代码,我无法判断是否可以使用 Web Workers。

无论如何,这应该为如何解决这个问题提供一些指导。

【讨论】:

  • 谢谢,问题正是你所说的,11ms 的 setTimeout 是有效的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-14
  • 1970-01-01
  • 1970-01-01
  • 2012-07-07
相关资源
最近更新 更多