4

Can setInterval result in other scripts in the page blocking?

I'm working on a project to convert a Gmail related bookmarklet into a Google Chrome extension. The bookmarklet uses the gmail greasemonkey API to interact with the gmail page. The JavaScript object for the API is one of the last parts of the Gmail page to load and is loaded into the page via XMLHttpRequest. Since I need access to this object, and global JavaScript variables are hidden from extension content scripts, I inject a script into the gmail page that polls for the variable's definition and then accesses it. I'm doing the polling using the setInterval function. This works about 80% of the time. The rest of the time the polling function keeps polling until reaching a limit I set and the greasemonkey API object is never defined in the page.

Injected script sample:

    var attemptCount = 0;

    var attemptLoad = function(callback) {

        if (typeof(gmonkey) != "undefined"){
            clearInterval(intervalId);  // unregister this function for interval execution.
            gmonkey.load('1.0', function (gmail) {
                self.gmail = gmail;
                if (callback) { callback(); }
            });
        }
        else {
            attemptCount ++;  
            console.log("Gmonkey not yet loaded: " + attemptCount );
            if (attemptCount > 30) {
                console.log("Could not fing Gmonkey in the gmail page after thirty seconds.  Aborting");
                clearInterval(intervalId);  // unregister this function for interval execution.
            };
        }
    };

    var intervalId = setInterval(function(){attemptLoad(callback);}, 1000);
B Sharp
  • 477
  • 8
  • 16
  • 1
    Well, during the execution of the callback, no other code (event handlers, timeout callbacks) can be executed, since JavaScript is single threaded... if that is what you mean. – Felix Kling Aug 15 '12 at 23:09

2 Answers2

14

Javascript is single threaded (except for web workers which we aren't talking about here). That means that as long as the regular javascript thread of execution is running, your setInterval() timer will not run until the regular javascript thread of execution is done.

Likewise, if your setInterval() handler is executing, no other javascript event handlers will fire until your setInterval() handler finishes executing it's current invocation.

So, as long as your setInterval() handler doesn't get stuck and run forever, it won't block other things from eventually running. It might delay them slightly, but they will still run as soon as the current setInterval() thread finishes.

Internally, the javascript engine uses a queue. When something wants to run (like an event handler or a setInterval() callback) and something is already running, it inserts an event into the queue. When the current javascript thread finishes execution, the JS engine checks the event queue and if there's something there, it picks the oldest event there and calls its event handler.

Here are a few other references on how the Javascript event system works:

How does JavaScript handle AJAX responses in the background?

Are calls to Javascript methods thread-safe or synchronized?

Do I need to be concerned with race conditions with asynchronous Javascript?

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thanks. I understand that no other scripts can run while the handler is running, but my expectation was that the execution of the handler would be very brief and that control would then be relinquished to whatever other scripts/events need to run until the 1 second interval elapsed and the handler was invoked again. Is there something I am missing in that? – B Sharp Aug 15 '12 at 23:31
  • 1
    @BSharp - that is the correct interpretation. If your timer script runs only briefly (like a few milliseconds), then all the rest of the time in the 1 second interval of your interval timer is available for all other event handlers or timers to run. – jfriend00 Aug 16 '12 at 04:13
  • This has to be the best setInterval() explanation on stackoverflow. – Daniel Viglione Jul 15 '16 at 18:31
5

setInterval and setTimeout are "polite", in that they don't fire when you think they would -- they fire any time the thread is clear, after the point you specify.

As such, the act of scheduling something won't stop something else from running -- it just sets itself to run at the end of the current queue, or at the end of the specified time (whichever is longer).

Two important caveats:

The first would be that setTimeout/setInterval have browser-specific minimums. Frequently, they're around 15ms. So if you request something every 1ms, the browser will actually schedule them to be every browser_min_ms (not a real variable) apart.

The second is that with setInterval, if the script in the callback takes LONGER than the interval, you can run into a trainwreck where the browser will keep queuing up a backlog of intervals.

function doExpensiveOperation () {
    var i = 0, l = 20000000;
    for (; i < l; i++) {
        doDOMStuffTheWrongWay(i);
    }
}


setInterval(doExpensiveOperation, 10);

BadTimes+=1;

But for your code specifically, there's nothing inherently wrong with what you're doing. Like I said, setInterval won't abort anything else from happening, it'll just inject itself into the next available slot.

I would probably recommend that you use setTimeout, for general-purpose stuff, but you're still doing a good job of keeping tabs of the interval and keeping it spaced out.

There may be something else going on, elsewhere in the code -- either in Google's delivery, or in your collection.

Norguard
  • 26,167
  • 5
  • 41
  • 49