I'd like a Web Worker which is deep in a call stack to be able to make a synchronous request to get information from the GUI.
The GUI itself is not blocked--it's able to process messages. But the JavaScript on the worker's stack is not written in async / await style. It is just a lot of synchronous code. So if the GUI tried to send a response back to the worker with a postMessage, that would just be stuck in the onmessage() queue.
I've found at least one hack that works in today's browsers. The worker can postMessage to the GUI for the information it wants--along with some sort of ID (e.g. a UUID). Then it can make a synchronous XMLHttpRequest--which is not deprecated on workers--to some server out on the web with that ID.
While the worker is waiting on that http request, the GUI processes the information request. When it's done, it does an XMLHttpRequest to POST to that same server with the ID and the data. The server then uses that information to fulfill the blocking request it is holding open for the worker. This fulfills the synchronous request.
It may seem hare-brained to outsource synchronization between the GUI and the worker to a server. But I'll do it if I have to, because it does not fit the use case to force the worker code to be written in asynchronous style. Also, I'm assuming that someday the browser will be able to do this kind of synchronization natively. But it looks like the one mechanism which could have been used--SharedArrayBuffer, has been disabled for the time being.
UPDATE circa late 2018: SharedArrayBuffer was re-enabled in Chrome for desktop v67. It's not back on for Android Chrome or other browsers yet, and might be a while.
(More bizarre options like compiling a JavaScript interpreter into the worker so the JS stack could be suspended and restarted at will are not on the table--not just due to size and performance, but the inability to debug the worker using the browser's developer tools.)
So...
Is there any way for a synchronous XMLHttpRequest to be fooled into making a request of something coming from within the browser itself (maybe via a custom link scheme?) If the GUI thread could directly answer an XMLHttpRequest that would cut out the middleman.
Could the same functionality be provided via some kind of plugin? I'm thinking maybe synchronization could be done as an abstraction. If someone doesn't have the plugin, it falls back to using the network as a synchronization surrogate. (And presumably if they ever re-enable SharedArrayBuffer, it could just use that.)
I'm wondering also if there is some kind stock JS-ready service which already implements the protocol for the echo server...if anyone knows of one. Seems quite easy to write.
XMLHttpRequest supports both synchronous and asynchronous communications. In general, however, asynchronous requests should be preferred to synchronous requests for performance reasons. Synchronous requests block the execution of code which causes "freezing" on the screen and an unresponsive user experience.
A web worker is a piece of browser functionality. It is the real OS threads that can be spawned in the background of your current page so that it can perform complex and resource-intensive tasks. Imagine that you have some large data to fetch from the server, or some complex rendering needs to be done on the UI.
Web Workers are a browser feature that allows scripts to be executed on a separate thread from the main execution thread of your web application. This allows the main thread of your web application to run without being blocked by slow scripts in your application.
Web Workers are primarily used for CPU-intensive tasks to be run in the background without any network connectivity required to work on the tasks.
I don't see a way to do what you're trying to do. Approaches that appear initially promising eventually run into hard problems.
fetch
In a comment, you suggested service workers as a possible solution. The service workers examples I've seen mention providing "custom responses to requests". However, all examples use the fetch
event to provide the custom response. AFAIK, it is produced only when you actually use the fetch
API specifically. An xhr won't generate a fetch event. (Yes, I've tried it and it did not work.) And you cannot just use fetch
in your specific situation instead of xhr because fetch does not operate synchronously. The specs for fetch
mention a "synchronous flag", but it is not part of the API.
Note that the fetch
API and the associated event are not specific to service workers so you could use fetch
in a plain worker, or elsewhere, if it solved your problem. You often see fetch
mentioned with service workers because service workers can be used for scenarios where regular workers cannot be used and some of those scenarios entail providing custom responses to fetch
requests.
Marinos An suggested in a comment using a fake XMLHttpRequest object. In most cases, that would work. Testing frameworks like Sinon provide fake XMLHttpRequest that allow testing code to have complete control over the responses that the code under test gets. However, it does not work for your use-case scenario. If your fake xhr implementation is implemented as one JavaScript object, and you try sending it to the worker, the worker will get a complete clone of it. Actions on the fake xhr performed inside the worker won't be seen outside the worker. Actions on the fake xhr performed outside the worker won't be seen inside the worker.
It is theoretically possible to work around the cloning issue by having the fake xhr consist of two objects: a front end through which requests are performed, and a backend through which fake responses are established. You could send the front end to the worker, but the front end and the back end would have to communicate with each other and this brings you right back to the communication problem you were trying to solve. If you could make the two parts of the fake xhr talk to each other in a way that allows you to fake synchronous xhr requests, then by the same token you would be able to solve the communication problem without the fake xhr.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With