The naive conceptual model for subscriptions is probably that they are processed after every update — which they may actually be — and that they can immediately start or stop listening for particular “events”. The latter part is what is potentially naive since if there are any queues involved anywhere either in the Elm runtime or on the browser side, there is a question about what happens for things that are queued for delivery but haven’t actually been delivered. For example, consider what the interaction needs to be or could be between the command querying the window’s size and the resize subscription if the window is in the midst of resizing. Unless the window size query command immediately invokes an update with its result, it is in a race condition with the notifications from the callback driving the subscription. Many commands act asynchronously but this one can’t.
Turning back to the case of mouse clicks, does the root level click notification happen at the same conceptual time as the element level notification or not? Since everything is serialized, the answer is practically not. The browser may well, however, be trying to make it appear to be so. For example, the browser could capture the list of potential callbacks to invoke at the point when it starts dealing with the event. That way changes to the callback structure won’t change the handling for this event.
Where this comes back to Elm is that the browser’s behavior for the event is theorized to be based on which callbacks Elm has registered at the time of the click. From Elm’s standpoint, Elm probably just has or doesn’t have a root level callback for window clicks. It doesn’t add and remove callbacks as the subscriptions change but only as the presence or absence of any click subscriptions changes. Changing what the subscriptions do changes how Elm handles the invocation of the click callback. So, in the NoOp case, Elm already had a click subscription and hence a registered callback and hence the browser picked that callback up as on the list of callbacks to invoke for the event. Without the NoOp, Elm responds to the click by gaining a click subscription and hence it registers a root-level click callback but that callback is too late to be captured by the browser as a potential handler for this particular event.
Finishing up and looking again at the broad question, what this seems to mean is that if Elm is going to the expense of calling subscriptions after every update and Elm is just responding to callbacks from outside rather than maintaining an internal queue of messages to be handled, then stopping a subscription will guarantee that you won’t receive a message for that subscription because it is already in flight. (It would be nice to have that be an explicit guarantee. I code defensively since it isn’t.) On the other hand, given browser behaviors around deciding which callbacks are called, it can be unpredictable when exactly a new subscription will be invoked.
Mark “Reader of Entrails”