coincident
coincident copied to clipboard
How to use coincident with sharedworkers?
I would like to be able to use coincident with Shared Workers, rather than normal Web Workers.
I do not know what would be involved in making this work, so do not have any suggestions. Though I do know that your legacy Proxied-Worker library seems to support Shared Workers and uses its port property.
Thanks!
Shared Workers can't have access to the window object ... would that be OK? I am thinking about a coincident/shared/main and coincident/shared/worker exports 🤔
Unfortunately I don't think I have any useful to say here - your knowledge and judgement far surpass mine. Whatever you think works best is surely best!
@nickchomey what I am asking is: do you expect the ability to deal with the window main thread object in there? 'cause AFAIK SharedWorker can be used cross tabs too, not just one, so the complexity around that is exponential.
Yes, I believe that the primary use case of Sharedworkers is to create a single worker that is used across tabs. Otherwise normal workers can be used.
Some use cases are for things like
- maintaining a single websocket connection
- sharing state across tabs, for lots of use cases
- single indexeddb writer
- surely much more
My impression is that we just need to pass messages/data between the tabs and shared worker, but I don't know if the window object would be needed.
(I'm somewhat new to JavaScript so just trying to get my bearings with all of this. But it is quite clear to me that a sharedworker would be the right tool for these sorts of jobs.)
Your proxied-worker library has support for sharedworker - can't you just implement the same mechanism? Or is there something fundamental that I am missing? (perhaps related to whether the sharedworker should have access to the tabs Dom, or even tabs have access to each other's Dom?)
so ... coincident/main and coincident/worker allow workers to invoke functions from main in a synchronous way and without blocking, while the main thread can invoke workers functions asynchronously (hence still without blocking the main thread).
enter coincident/window/main and coincident/window/worker ... in this case, for the main thread the contract is the same but the worker can access the page window or globalThis object synchronously, so that a worker can do document.body.textContent = "worker riding the main thread" or even window.location.ref = "else.where" to redirect, which is quite powerful and never existent before coincident project landed on GitHub.
This functionality never existed before means that proxied-worker library never cared about it and never used the primitives I am using in here, most notably: Atomics.
There is even a fallback to a ServiceWorker based on sabayon Atomics and SharedArrayBufer polyfill/orchestration that was never considered in proxied-worker neither as that module is fairly old and limited, or better, it's not based on the same primitives this module is based on ... which are unique in the whole JS ecosystem and mostly misunderstood or underestimated because "too magic to trust" out there (their problem, imho).
As summary, once a SharedWorker implementation lands I believe the discussion should be around these topics:
- should it expose all attached
windowobjects from all tabs that connected to it? I think no - should it behave like a WebSocket where the shared worker can broadcast things to all attached tabs? I think maybe as that's some extra complexity to consider, but not impossible to achieve
- should it be a side-channel for Service Workers VS tabs communication? I have no idea if that's even possible
Last, but not least, the IndexedDB issue you mentioned is not necessarily an issue because, like cookies, or localStorage, IndexedDB is attached to the domain: you bootstrap a db on the same domain/path, you get the same db (last time I've checked).
To share states across tabs you have localStorage too, which also offers storage event to communicate to all same-tabs/domain things get notified when a key changed in there ... and I believe this is the most reactive and desirable pattern you want because it's confined on the main thread but it allows any coincident worker to reach out for window.localStorage.setItem("state", JSON.stringify(state)) so that no matter which worker is doing what, all tabs will react to that change done by maybe the only page that bootstrapped, or requires, a worker.
All I am saying is that it looks like your desire for Shared Worker lacks some background around how other primitives work in the JS world, and hopefully some of these hints would help you forward before I find the time and the will to implement what you are asking as it's not super straight forward, the orchestration across multiple sources is not super trivial neither, and I am not fully sure about what should work and should absolutely be banned from that implementation.
If I could have concrete previous work or uses cases that wouldn't work better with just IndexedDB or localStorage, I might at least try to formulate an opinion around this request.
I hope I have explained myself decently enough here, happy to expand on any topic you like.
Thanks, that's helpful! I'll ponder it all as I explore how to implement what I need. Though, I'm likely to use web locks (as mentioned in the previous issue that I closed) so that I can have this functionality on Android chromium (which doesn't yet have shared workers).
In fact, that might be reason enough to not bother with this. Either way, don't feel any pressure from me to work on this soon, or even at all. We're all grateful for everything you do.
After refining this module lately to avoid every single possible detail that could interfere with each other when running in a multiple workers or servers scenario (no more shared HEAP, dedicated strong-random channel per each worker) my idea is that a Shared Worker is just an indirection of a shared window ... if multiple workers can interact with the same window object without leaking or interfering with each other, what stops a Shared Worker to provide exactly the same functionalities a window on the main would?
All I am saying is that a Shared Worker is, by definition, a main to target, just like a server, so that in theory you could have a single coincident/window/main on your main thread, a coincident/window/worker on your worker that imports also a coincident/shared/worker so that such Shared Worker can import a coincident/shared/main ... the result is that you have separate modules, and concerns, to synchronously ask operations from both your main tab UI but also from the Shared Worker and it's up to you to use such worker to orchestrate the dance around these two ... would this somehow work for you?
In few words:
- each tab bootstraps a worker via coincident
- each worker, per each tab, bootstraps a Shared Worker per each main
- the Shared Worker is bootstrapped only once but every other tab would follow the same approach
- the worker that bootstrapped the Shared Worker contains logic to forward operations that involve both the SW and the window on the page
- you end up with N tabs bootstrapping N workers that orchestrate 1 Shared Worker ...
does this make sense to you and do you think this would be desired? In all this I am not factoring in the complexity related to the server offer but it's also true that the server current logic is what makes me confident the current proposed approach might work ... as you can see, it's convoluted behind the scene, but it can be easy as cake on the user ... I still need to understand use cases for all this beside a sync update across tabs ... I mean, why do you need multiple tabs to start with? 😅
P.S. the more I think about this request, the more I feel like it's just an indirection of what I do already with server export ... server in that case is WebSocket driven and it expects either NodeJS or Bun to deal with, but maybe I can improve that server story to pass a Shared Worker end point instead, and call it a day ... this approach would not make it possible to have both Bun or NodeJS and the Shared Worker in the same equation, which is why I am asking if the bi-import approach per worker could be a solution.
let me simplify my question: are you expecting to use just a Shared Worker instead of a Worker from any tab main? 'cause that is a bit of a stretch of what I can do in terms of time to dedicate to move this forward ... I hope you can understand that.
and then again ... I see SharedWorker is not available in workes ... now that's a bummer: https://webreflection.github.io/is-it-worker/?SharedWorker
Thanks for all of the thought about this!
is a bit of a stretch of what I can do in terms of time to dedicate to move this forward
First, I want to make it clear that you have no obligation to work on this! I opened it more as a placeholder than anything.
Second, as I mentioned, perhaps this isn't worth pursuing until Android Chromium (over 50% of web usage) gets support for sharedworkers. Or, perhaps showing a clear and popular use case for sharedworkers will encourage them to implement it...
Third, i have to admit that my knowledge of the inner workings of workers and other web APIs is limited, so I can't really comment on whether your proposal is appropriate. Dummies like me use libraries from experts like you so that we don't need to become experts ;)
What you've proposed seems good though!
As for the question of using just a sharedworker from each tab rather than a worker, I'm not sure. It seems like a reasonable and normal use case though
And as for you not being able to access a sharedworker from a normal worker, that is surprising. The first paragraph of the mdn doc says that they can be accessed from anywhere. Perhaps, however, the limitation you're seeing is due to how Coincident works?
And as for why would I use multiple tabs? I'm not sure how to answer that to be honest - I can't be the only person who often opens multiple tabs of the same site while browsing. If there's a mechanism in each page that relies on common data (eg web sockets) then it would be prudent - for the sake of browser and backend server cpu and memory resources, as well as bandwidth usage - to have them all share a single, consistent source.
The bun/node/server stuff seems irrelevant to me here - I think this is all client browser-side. But if you see ways to integrate/harmonize that stuff, that would be great.
Is this helpful?
Perhaps, however, the limitation you're seeing is due to how Coincident works?
this link simply asks a worker to tell if something is available on its global scope and the answer is no, no coincident involved: https://webreflection.github.io/is-it-worker/?SharedWorker
P.S. I don't read Available in Workers in this page anywhere: https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker
for the sake of browser and backend server cpu and memory resources, as well as bandwidth usage - to have them all share a single, consistent source
well ... no ... a server is the source of truth, usually, a shared worker is not necessarily persistent so it's not really a source of truth, you need extra orchestration to make it one, but then again Service Workers are a better primitive.
Maybe all your pain-points will be solved via a Service Worker instead? 🤔
P.S. I don't read Available in Workers in this page anywhere: https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker
It is in the very first sentence
The SharedWorker interface represents a specific kind of worker that can be accessed from several browsing contexts, such as several windows, iframes or even workers
well ... no ... a server is the source of truth, usually, a shared worker is not necessarily persistent so it's not really a source of truth, you need extra orchestration to make it one, but then again Service Workers are a better primitive.
This isn't about persistence. It's about not having (in one popular use case) redundant connections to the backend source of truth - particularly when the backend is pushing updates to the browser.
Also, I and many others do not use node/bun/deno/js in the backend.
Maybe all your pain-points will be solved via a Service Worker instead? 🤔
Service workers have unreliable/murky support for websockets and sse connections. They're ephemeral. It is more appropriate to just initiate a single connection across all tabs.
It is all a very common use case of sharedworkers, and even mentioned in that same mdn doc. And many other places.
The SharedWorker interface represents a specific kind of worker that can be accessed from several browsing contexts, such as several windows, iframes or even workers
that deserves an issue filed to MDN as my test page would beg otherwise ... I've missed that, sorry.
It is all a very common use case of sharedworkers, and even mentioned in that same mdn doc. And many other places.
sure, but if I can't create a SharedWorker from a Worker I am afraid there's not much I can move on in here ... I wish we could clarify this aspect though, as it seems misleading from that test page (which does nothing but report what's found in the global context for that entry) so if it's that page doing something weird in the little code it uses, I'd be happy to improve that: https://github.com/WebReflection/is-it-worker/blob/main/worker.js
just FYI that (my) page doesn't lie ... https://stackoverflow.com/a/30831231
if current state is confirmed (no SharedWorker on Workers ... and I think it is) I need to think upside-down the dance ... or test Atomics would work from a shared worked but at that point I need to orchestrate each main that is connecting to one of those so yeah, we're back to exponential complexity to provide the same ease of API and I don't have this requirement at the moment so my time is really low around this, I am afraid.
Ok, perhaps the confusion/conflict is over where to create the SharedWorker from.
You've been focused on creating a SharedWorker from within a normal Worker. I assumed that the SharedWorker would be created in the main thread, just as is normally done for Web Workers. Apologies if I said anything that confused this.
It seems to be possible to communicate between Main<->SharedWorker and Worker<->SharedWorker via
Coincident seemingly exists to avoid using those APIs, instead using SharedArrayBuffers, Atomics etc... So, the question is whether it can use those to communicate Main<->SharedWorker and Worker<->SharedWorker?
Here's an article that doesn't quite answer the question, but may be useful SharedArrayBuffer and Atomics
Do you think it might be possible?
And, again, even if it is possible, there's no obligation whatsoever to actually work on it. I was simply bringing up the idea for consideration at some point.
But I do think there's a lot of legitimate use-cases for something like this
- maintaining a single websocket or SSE connection with a backend
- single connection to IndexedDB
- single WASM instance
- minimize the memory requirements of using workers (e.g. rather than each tab creating the same Worker to do some sort of computation, they could all use the same one).
In fact, this last case seems like it would be the most common and, perhaps even, compelling. I imagine that a significant portion of scripts that are run in many duplicate/redundant Workers could actually be run in a single SharedWorker.
uhm ... the thing is, you never need more than a Worker per page/tab but you also could:
- reuse the same WebSocket created in main,
coincident/serveruses main to orchestrate a sync dance in the worker through the Worker->Main->WS->Server->Worker channel - you can connect multiple workers to the same IndexedDB from main ... the main can use async APIs after all and currently it is the shared thread just like SharedWroker would be
- the single WASM instance ... I am not fully sure about that ... you execute different code from different tabs and having one tab changing the env you are working on for another tab seems like footgun prone .... but I can see what you are after
- having just a shared worker per each main might indeed help with too many tabs and too many workers but again, this scenario is for people incapable of keeping their tabs count low and I can see a lot of sync issues could happen although I am speculating
So, the SharedWorker from main requires both sabayon and coincident changes / updates / features that were never in but maybe I can recycle more code than I think for SharedWorkers and provide a very similar dance I already do with workers ... I need to play around and see but my time to do so is extremely limited.
Let's see ... still MDN should change that "available everywhere" thing because it's extremely confusing to me.
Update around this topic in here: https://github.com/DallasHoff/sqlocal/issues/39#issuecomment-2577418644
Let's see how that goes ... I am not super happy about the current state of SharedWorker and yet I think it's an awesome primitive we've been all sleeping on for too long so let's try to find out what is it that we actually need/want from it, and deliver its best potentials!
coincident doesn't seem to be a good fit for that though, it uses a polyfill that is basically just a Worker so it doesn't fully solve the issue + it's already very complex, as a module, and making it even more convoluted might lead to more breaking things instead of greater wonders.
Thanks! I'll explore that issue.
I'm brand new to all things web, but am absolutely dumbfounded that seemingly no one uses Shared Workers - it just seems so beautiful and full of potential. It has to be more than just it's absence in Chromium for Android...
I'd recommend that you show your support for that here (https://issues.chromium.org/issues/40290702), and share it with others however you can. They've basically said it's a matter of when, not if. If more people can demonstrate that they want/need it, maybe they'll be more inclined to implement it. Even iOS has it now!
Even iOS has it now!
that's the thing ... Chrome/ium team doesn't like to be behind iOS so the fact it works already on iOS will be the deal for them ... no matter how many users will upvote that issue ... imho 😅
@nickchomey I've tried anyway https://issues.chromium.org/issues/40290702#comment70 ... but I am not holding my breath though, these "hard" decisions usually have ads reasons behind the scene ... I hope this is not the case though, yet I am not sure anything will change in a timely fashion.
I can't figure out how ads would play into this, but it's entirely possible... Anyway, no harm in showing support!
But, yes, I don't expect much to come from it. Best to solve the problem as best we can. It looks like you're making good progress in accordant and others are working away at it too. Hopefully a nice solution gets developed soon!
@nickchomey
I can't figure out how ads would play into this, but it's entirely possible
my mind is biased and twisted after 5 years working on Ads blockers' core so ... I might be hallucinating but to me the ability to persist across browsers undesired content out of the box means that Sharedworker would do what extensions do already but without the extension per tab overhead ... there, I wrote it!
laughable ... sure, but imagine browsers' vendors control extensions content but, due the nature and constraints around SharedWorker functionality (on host servers) they can't control how many things such primitive could do in the long run ... of course the site offering SW and also providing Ads won't ever allow its own SW to hurt their business, but SW can be extremely powerful for many things, and forever persistent until one tab reaching those is opened ... ServiceWorker, the other SW in the game, can get randomly swtiched on and off instead, no ports attached 🤷
update I am afraid after my entire rewrite of this library the SharedWorker idea has faded away and I've described why while closing the related PR: https://github.com/WebReflection/coincident/pull/52#issuecomment-2828560792
Mostly because the scope of related issues is completely outside this module goal, but once all those issues will be solved this module can also work with it and yet I've filed issues in Firefox around MessageChannel being broken with Atomics, the whole Sabayon dance was overkill for something never used (atomics timeout) so the current code is smaller, faster, and way better orchestrated to do one thing and one thing right.
I am not excluding that in the future I will take another look at what SharedWorker could unleash/deliver combined with coincident but right now all I care is that performance is the best I can have and that complexity has been reduced by far.