13

I have a Web Worker. I wish to make periodic network requests with it. One thing I particularly want is to make these requests even if the main JS execution thread is blocked (eg by a window.alert). I'm using Chrome 38.

However, when I attempt to make network requests in the worker, the requests appear to be blocked by the UI thread. Here is a contrived example to illustrate the problem:

base.js:

var worker = new Worker("/worker.js");

setTimeout(function() {
    console.log("begin blocking");
    var startDt = new Date();
    var blockPeriod = 5000;
    var a;
    // Obviously we'd never actually do this, but this while loop
    // is a convenient way to create the problem case (a blocked main
    // thread).
    while ((new Date() - startDt) < blockPeriod) {
        a = 0;
    }
    console.log("stop blocking");
}, 3000);

worker.js:

var requestInterval = 1000;

var sendRequest = function() {
    console.log("Send request interval");

    var request = new XMLHttpRequest();
    request.open("GET", "/ping", true);

    request.onload = function() {
        if (request.status === 200){
            console.log(request.responseText)
        } else {
            console.log(request.status)
        }
    };

    request.onerror = function() {
        console.log("error")
    };

    request.send();

    setTimeout(sendRequest, requestInterval);
}

sendRequest();

The result I'm seeing is that we see successful HTTP requests for three seconds, until the blocking begins. At this point, we don't see anything logged to the console until the blocking ends, at which point we see five "Send request interval"s followed by 5 logs of the response, like so:

Send request interval
{"pong": true}
Send request interval 
{"pong": true} 
Send request interval
{"pong": true}
Send request interval
{"pong": true}
begin blocking
stop blocking
5x Send request interval
5x {"pong": true}
Send request interval
{"pong": true}

I also see in my server logs that no requests are made in that blocking time, then those five requests are all received roughly simultaneously at the end of the blocking period.

Given that "Send request interval" occurs five times in a row, the worker is evidently continuing to execute: if it weren't, it wouldn't make it through to queue up the next iteration. I've also found that if I block by triggering a window.alert instead of spinning in a loop, I get the log messages from the beginning of sendRequest at 1 second intervals, and then get the response handler log messages in a large batch as soon as I stop blocking.

In Firefox, the background thread seems to stop entirely in this case (I don't get that same batch of five requests queued up during the blocked period). However, I'm only targeting Chrome in this case (and I ultimately want to use WebSockets which don't even work in Firefox Workers), so I'm not really interested in that.

All added together, this leads me to believe that there are some classes of activity in Web Workers which are blocked by the spawning thread, and some which are not (I originally saw this same behavior with WebSockets). Concretely, I'd like to know (if anyone does know):

  1. What Worker activity is blocked by the main thread in Chrome?
  2. Is there a way to work around this? I'd very much like to be able to able to establish a WebSocket connection in a Worker, and then continue to PING/PONG back and forth, even if something (such as an alert/confirm) does block the main thread.
  3. Is this all nonsense, and am I just doing something stupid?
heliotrope
  • 981
  • 7
  • 14
  • Yeah, it [looks like the event loop doesn't stop](http://stackoverflow.com/a/2734311/1048572) during an `alert` :-) – Bergi Dec 16 '14 at 22:02
  • Bergi, thanks for the reply. However, the whole idea of web workers is that they operate in a background thread, and don't interfere with the UI (bobince even mentions this caveat in the answer you linked to). And that's evidently the case to some extent: the examples I've outlined above show that to some extent, they are executed even when the main thread is blocked. My big question is where the line is between what is and is not blocked by the main thread. – heliotrope Dec 16 '14 at 22:10
  • "Non-blocking doesn't necessarily mean concurrent". Source article: http://code.tutsplus.com/tutorials/getting-started-with-web-workers--net-27667 – sergolius Dec 16 '14 at 22:12
  • @sergiolius, I'm not sure I follow. That article seems to be claiming that web workers *are* a way to achieve concurrency. My problem is that there seem to be undocumented limits on that concurrency. – heliotrope Dec 16 '14 at 22:18
  • @heliotrope, can explain that. I create local server and test your code. Main thread really blocked `XMLHttpRequest` from webworker. – sergolius Dec 17 '14 at 08:55
  • @sergolius yes, the main thread did block the XMLHttpRequest that was initiated by the worker. My problem is that based on my understanding of web workers, that shouldn't happen. Moreover, I haven't been able to find any documentation of what web worker activity is blocked by the main thread, and why. As a consequence, the questions I'm asking are whether there are any solutions, or at the very least whether there's any documentation anywhere about what functionality is affected by this. – heliotrope Dec 17 '14 at 20:49
  • 3
    I see. I misunderstood the question. In your production code are you attempting to use console.log() in the worker? Your problem may be as simple as the console output is running on the main thread which is busy with whatever long running process you've started. – Shawn Whinnery Dec 17 '14 at 22:20
  • @ShawnWhinnery I'm not, and I've also confirmed from my server logs that even in this example case, the requests are never issued (it isn't just that the logging in the callback is delayed). For reference, the console.logs *are* delayed, but my server logs indicate that the requests are only initiated after the main thread is unblocked (and this is also suggested by the sequencing of the messages logged to the console). – heliotrope Dec 17 '14 at 22:44

3 Answers3

6

Your observation is correct. When the UI thread is blocked, network calls aren't dispatched.

Even worse, Chrome has the best behavior of the bunch. When a worker makes a XHR request when the UI thread is blocked:

  • Chrome: all requests are queued. The browser will not actually issue the requests until the UI thread unblocks. On the plus side, the worker thread is still free to run.
  • Firefox: new XMLHttpRequest() blocks until the UI thread unblocks.
  • IE: xhr.open() blocks until the UI thread unblocks.

While Chrome fortunately does not cause a worker thread to stop and wait (even though it won't get any data), Firefox and IE will cause a worker thread to wait on the UI thread when you try to make a XHR request.

There is no way to work around this; you're beholden to the browser to make requests on your behalf. I haven't done any testing with WebSockets, but they may deliver events even if the UI thread is blocked. At worst, the received messages would queue until the UI thread unblocks.

josh3736
  • 139,160
  • 33
  • 216
  • 263
  • 1
    Welp, there goes that. In my testing, the same issue occurs with WebSockets too (even if the connection is already established, messages can't be passed back and forth whilst the main thread is blocked). More generally, do you have any insight into why network calls aren't dispatched whilst the UI thread is blocked? Perfectly reasonable if not, just curious... – heliotrope Dec 17 '14 at 23:35
  • 1
    Afraid I can't really give you the exact rationale behind why it works the way it does -- or if it's even intentional. Anyway, to solve your issue, have you considered using a lightbox modal and/or [`Notification`s](https://developer.mozilla.org/en-US/docs/Web/API/notification) instead of `alert()`? – josh3736 Dec 17 '14 at 23:46
  • 1
    Ah, so it goes (was just curious). Yep, I have thought about it, but sadly there are a few cases in the relevant application where I can't avoid a blocking alert (eg hooking into `pageunload` events), and I'm also just generally looking to maximize resilience against unexpected blocking :/. Thank you very much for your response! – heliotrope Dec 18 '14 at 00:08
2

In case anyone stumbles across this, this behavior is confirmed as a bug (to the loose definition of "bug" as "does not behave as it ought to") in Blink, as of February 2015:

https://code.google.com/p/chromium/issues/detail?id=443374

heliotrope
  • 981
  • 7
  • 14
1

I'm having the same issue, with a Web Worker that performs a sort of a keep-alive: it periodically pings the server, to inform that the page is still alive. I'm also using console.log in the worker, but i'm sure this is not the cause.

After some investigations, I can state that the problem can address two different situations:

  1. a long-time UI operation is blocking main thread and worker's requests are not executed. This is the case of sample illustrated by heliotrope. As of September 2018, the problem occurs only on Firefox and IE, since Chrome and Edge can correctly handle activities on worker when the main thread is blocked.

To solve this issue, I'm thinking at sending a special ping to the server before starting any long-time operation, to inform that he won't receive anything from me, until the operation is finished.

  1. a long-time asynchronous ajax call is being performed by main thread and worker's requests are queued. Some setting on the server is preventing from multiple ajax calls to run in parallel: other requests are queued until the first ajax call completes. The problem is server-specific, hence it does not depend on any browser in particular.

This problem is, in my case, due to ASP.NET session state locking: the ASP.NET pipeline will not process requests belonging to the same session concurrently but queues them, and executes them serially. Here is a detailed link: http://tech-journals.com/jonow/2011/10/22/the-downsides-of-asp-net-session-state.

Marking controller's session state as ReadOnly will solve the problem, while completely disabling session state in Web.config (<sessionState mode="Off" />) will seriously improve performance of the whole application.

jeanie77
  • 515
  • 1
  • 4
  • 22