"The server always asks for new location data, every 30 seconds, even when no one is listening."
...
"Our task is to pause the timer when there are no websocket connections and start the timer when a client connects."
That sounds like asking for trouble. Maybe there's something I am not understanding, but why not have the timer run continuously and have the timer action check whether it needs to request location data?
Yes, that will cause the process to wake up every 30 seconds even if there is no action, but unless circumstances are very special that won't be a significant problem.
Please pardon my inexperience, but at what scale does this become significant? Where I've worked readability/clarity/technical-debt have always taken more primary positions as relates to expected costs.
Would you mind linking a reference or example describing the trade-offs in this?
A great effort was untertaken to wean kernels from relying on regular ticks[0][1]. This benefits power consumption on mobile devices but also makes it easier to dedicate some cores 100% to latency-sensitive tasks.
Specifically on windows applications have been optimized to make less use of the QueryPerformanceCounter API because it causes the kernel to wake up cores more frequently, again increasing power consumption. Browsers also clamp the frequency of timer wakeups in background tabs to reduce wasted CPU cycles.
A javascript in a browser waking up once a second doesn't matter much. But hundreds of browser scripts and smartphone apps all waking up once a second at different offsets effectively means waking up every few milliseconds.
The central problem of this post is not whether the timer is running continually. The central problem is how does the timer know when to stop/start making requests.
It's a problem because knowing this depends on access to information that is trapped in a different context. For example, the number of closed connections is only knowable within the context of the connection event handler. So how do you share this information with the timer?
One approach is to mutate global state. You move the information into a shared context where it's accessible everywhere. A second option, which is the focus of this post, is to look for better abstractions. In this case, those are abstractions over time. And when you have those abstractions, you can eliminate the global state.
A question unrelated to your programming concepts: If you only query your source when a request comes in, doesn't the first requester get old data? This is a problem I have faced before.
I get why removing global state is a good thing in general, but here you are replacing it with a whole lot of complexity for not a particularly clear reason. Is tracking the user count with an integer dangerous in this case (Have you run into problems with it)? Is your solution not using global state anyway, just wrapped in a library?
All in all, are the abstractions (a bunch of new concepts plus a new library) worth it to just replace an integer?
function timerFunc() {
if (connectionCounter) get results();
}
socket.on('connect', function() { connectCounter++; });
socket.on('disconnect', function() { connectCounter--; });
The real problem is you want to start and stop the timer when someone connects. Just keep it simple and keep it going, but not doing anything if there is nobody connected, like above.
That always depends on whether whatever is contained within the abstraction is something that might change. Sigh, humor.
Leaving it as a primitive basically says 'you may have to hack the programming language or framework in order to modify code that depends on this piece of the logic'. Global state is something you can manage in terms of modular components. Allows for dynamic orchestration of components.
It's more the long term issue that keeps repeating (more humor) than it is just replacing an integer. Being able to decide 'is this component something that needs to be reasoned about in it's own independent logical layer from the rest of the system or is it not' or 'does this component relate to other components in a well defined way'.
Connect is an action, an observable action has occurred. An abstraction can trigger a behavior to other abstractions through that predefined relation. That's the point.
Very nice explanation. Gives a good insight how you can "think in observables". Two small things 1. instead of merging zero$ into connectionClosed$ you can use the startWith() method. 2. Instead of using the head method to pluck the first item of an array you could use array destructuring. (But I can understand that you wouldn't do it in this to limit the amount of "new" concepts)
16 comments
[ 3.0 ms ] story [ 51.0 ms ] thread"The server always asks for new location data, every 30 seconds, even when no one is listening."
...
"Our task is to pause the timer when there are no websocket connections and start the timer when a client connects."
That sounds like asking for trouble. Maybe there's something I am not understanding, but why not have the timer run continuously and have the timer action check whether it needs to request location data?
Yes, that will cause the process to wake up every 30 seconds even if there is no action, but unless circumstances are very special that won't be a significant problem.
Would you mind linking a reference or example describing the trade-offs in this?
A javascript in a browser waking up once a second doesn't matter much. But hundreds of browser scripts and smartphone apps all waking up once a second at different offsets effectively means waking up every few milliseconds.
[0] https://lwn.net/Articles/549580/ [1] https://en.wikipedia.org/wiki/Tickless_kernel
The central problem of this post is not whether the timer is running continually. The central problem is how does the timer know when to stop/start making requests.
It's a problem because knowing this depends on access to information that is trapped in a different context. For example, the number of closed connections is only knowable within the context of the connection event handler. So how do you share this information with the timer?
One approach is to mutate global state. You move the information into a shared context where it's accessible everywhere. A second option, which is the focus of this post, is to look for better abstractions. In this case, those are abstractions over time. And when you have those abstractions, you can eliminate the global state.
Or put your server in front of a CDN / Nginx, set cache headers for 30 seconds ;)
All in all, are the abstractions (a bunch of new concepts plus a new library) worth it to just replace an integer?
The real problem is you want to start and stop the timer when someone connects. Just keep it simple and keep it going, but not doing anything if there is nobody connected, like above.Leaving it as a primitive basically says 'you may have to hack the programming language or framework in order to modify code that depends on this piece of the logic'. Global state is something you can manage in terms of modular components. Allows for dynamic orchestration of components.
It's more the long term issue that keeps repeating (more humor) than it is just replacing an integer. Being able to decide 'is this component something that needs to be reasoned about in it's own independent logical layer from the rest of the system or is it not' or 'does this component relate to other components in a well defined way'.
Connect is an action, an observable action has occurred. An abstraction can trigger a behavior to other abstractions through that predefined relation. That's the point.