Can JavaScript's setInterval block thread exec

2019-04-15 05:54发布

问题:

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

回答1:

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?



回答2:

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.