最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

jquery - Why does a javascript function work in firefox but not work on Chrome or IE - Stack Overflow

programmeradmin3浏览0评论

I have created a method to start an activity indicator and a different method to stop the given indicator. Then when doing tasks that are time consuming, I call the start method before and the stop method after. An example of the call is shown below:

// Show loading
viewControllerShowAjax();

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

// Hide loading
viewControllerHideAjax();

This is working on firefox, but not working on either IE or Chrome. Is there a fix for this?

Edit: Below are the functions that I am using:

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");
}

Edit: For more information regarding the problem; I have found that if the hide function is not called for the activity indicator, then it will appear after the time consuming task is pleted. Again this is the behaviour in both IE and Chrome but not firefox.

I have created a method to start an activity indicator and a different method to stop the given indicator. Then when doing tasks that are time consuming, I call the start method before and the stop method after. An example of the call is shown below:

// Show loading
viewControllerShowAjax();

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

// Hide loading
viewControllerHideAjax();

This is working on firefox, but not working on either IE or Chrome. Is there a fix for this?

Edit: Below are the functions that I am using:

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");
}

Edit: For more information regarding the problem; I have found that if the hide function is not called for the activity indicator, then it will appear after the time consuming task is pleted. Again this is the behaviour in both IE and Chrome but not firefox.

Share Improve this question edited Jul 17, 2013 at 17:30 justkash asked Jul 16, 2013 at 15:23 justkashjustkash 7092 gold badges13 silver badges30 bronze badges 10
  • You need to provide more information about the "activity indicator". How specifically does it indicate activity? – EricLaw Commented Jul 16, 2013 at 15:24
  • It depends on exactly what those functions do. – Pointy Commented Jul 16, 2013 at 15:24
  • could you post some of the code of those functions so we can help you better? or even more helpfull would be to have a jsfiddle example – Samuel Lopez Commented Jul 16, 2013 at 15:29
  • There is no way this works in Firefox either. Maybe you were testing cached code or something? async jsfiddle – Esailija Commented Jul 16, 2013 at 15:41
  • @Esailija It did work with firefox, but I am not sure why it does and why it doesn't on either chrome or ie? Is there a different way that I am supposed to do this? – justkash Commented Jul 16, 2013 at 15:45
 |  Show 5 more ments

1 Answer 1

Reset to default 10 +250

Probable cause

The reason that this doesn't work is that JavaScript is single threaded.

The time-consuming function app.timeConsumingFunction() will block the UI from updating in Chrome and IE.

The reason for it to work in Firefox is simply because FF has a different implementation that allow UI events to execute in-between so to speak (Canary and I believe Chrome as well has an experimental mode that allow UI updates to be executed on a separate thread).

A browser in general cues up different events such as paint events. Then this queue is traversed and executed event by event.

However, if a queue-entry is for example executing the context for a time consuming function, the queue won't have a "breathing space" to continue with the other events in that queue.

That would be among others executing paint events that would otherwise paint the spinner. As it is blocked the spinner won't appear until the time consuming function finishes.

Possible solution using setTimeout

The way to solve this so it works is to part up the execution using either setTimeout or a Web Worker (see below for Web Worker example).

You could for instance try this to see if it improves current code (which I doubt but worth a try as it is simple) - as there is not enough code in the post to actually test this, consider it theory. Try by replacing this line:

app.timeConsumingFunction();

with

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

If that is not enough you will need to do a similar setup but with the internals of the function (which you don't show) which you divide, if possible, into parts that handles different stages of the operations. A skeleton example could be:

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);
}

The 0 argument to setTimeout tells the browser to queue this event at first available chance. This might not always work but you can use an interval of about 11 milliseconds instead which is close to the minimum available queue slice.

By splitting up the operations like this you will allow the browser to execute for example paint events in between and therefor be able to see the spinner.

Possible solution using Web Workers

Without knowing your code I can not say Web Workers are usable in this scenario as there are certain restrictions for those such as no DOM access, no storage access etc. The support for Web Workers is however excellent and I would remend this approach if possible with your code (ref. restrictions).

But if you do pure calculations consider Web Workers as an option:
https://developer.mozilla/en-US/docs/Web/Guide/Performance/Using_web_workers

A bare-bone example:

In your main script you can allocate a Web Worker like this:

var ww = new Worker(urlToWebWorkerScriptHere);

Web Workers municates through messages so on "our" side we listen to the message event:

ww.onmessage = function (e) {

    var msg = e.data;

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

The Web Worker uses a separate script which is either loaded as a separate JS-file or you can use a little trick to inline it with the HTML code (shown in the fiddle below).

The script is executed in an isolated context and is where all the calculations take place. When done it signals our main script:

onmessage = function(e) {

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

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

        /// the time consuming stuff..

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

And to start the calculation you would do this:

// Show loading
viewControllerShowAjax();

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

(the hide is called when the 'Done!' message is returned from worker).

You can of course send forth and back many messages, for example to return partly results and to trigger next part of the calculation (or operation).

I have made a simple demo in the following fiddle - notice that you can click the right button even if the loop in the fiddle is doing busy-looping/"heavy work" (in the example you can also see how to inline the Web Worker script instead of loading it separately):

WEB WORKER DEMO

But again, without seeing your code for the work I cannot tell if Web Workers can be used or not.

In any case this should give some pointers to how to solve this.

发布评论

评论列表(0)

  1. 暂无评论