Should an event listener be called if it is attached after the event has already fired? What if the event will only be fired once?
The first example that comes to mind is the ready
event in jQuery. The following snippet, when evaluated after the page has loaded, will still call the callback:
$(document).ready(function () {
console.log("Works.");
});
The alternative to this behaviour could be a flag that is set when the page is loaded, forcing the consumer of the API to check to see if the event has already happened and act accordingly:
if (document.readyState == "complete") {
console.log("Works.");
} else {
$(document).ready(function () {
console.log("Works.");
});
}
While the above example is in the context of a web page loading where anything and everything (usually) needs to happen after the page has completely loaded, the same arguments could be made for any single component in an application, which has “singleton” events (load
, start
, end
, etc). An example of a single component could be a map with a load
event that fires to specify that the map that has loaded:
map.on("load", function () {
console.log("The map has loaded.");
});
The above example comes from the ArcGIS API for JavaScript, where that event is fired only once, and if an a consumer “waits” for the map to load after the map is already loaded, the listener will never be called. The desired result requires checking the state of the map before the listener is attached:
if (map.loaded) {
console.log("The map has loaded.");
} else {
map.on("load", function () {
console.log("The map has loaded.");
});
}
Which behaviour is correct, requiring the consumer to check if an event has already fired or always calling the callback?
1
That depends upon whether it is really an Event or State Notification (which is what ready is). If it is an event, then you don’t tell the listener about all prior events. If it is State Notification, then you tell them immediately after they have subscribed and you are in the specified state.
The tricky bit will be where it can be either a state or an event, depending upon how you look at it — at worst, that is explained by the documentaion. At best you pick a good name that makes it clear which you mean.
4
In general, no.
Event listeners should not be called if they are attached after the event has already fired. “You snooze, you lose.”
There are some important exceptions. $(document).ready()
is perhaps the perfect example–a event prior to which the entire evaluation context is not reliably or completely established, and which serves as a “full function processing begins here” marker.
If however you set the rule that an event handler should be fired even if it is installed and/or activated after the event occurs, then you have declared that all events should or must be buffered from the beginning to the end of the program run, for every conceivable event. Else, there might be event handlers established on some event, somewhere down the road, that you don’t have sufficient information to know if you should fire when it’s established. That means you’d have to instrument every possible event source and infinitely buffer every one of them too. That takes event processing from lazy evaluation (and all of its performance benefits) all the way through eager evaluation and onto whatever is on the far end of the spectrum. Frenetic evaluation based on catastrophized assumptions about what events might need to be handled later?
Events tend to be transient and numerous. Setting rules that require each be buffered, without limitation, on the possibility that they’ll eventually be tapped–it’s performance suicide. Furthermore, there’s no great requirement for doing so, since handlers can generally be established early in program life.
The exceptions to the rule are edge cases–document or subsystem loads, for example, that are essential, unique, major events that are not otherwise captureable or handleable.
This is very close to your “singleton” events. Another group that might need special handling are significant errors, security events, or status changes that have an exceptional need to be flagged even if there are no subscribers to the flag at the moment the event occurred.
One final note: A “ready” event is subtly different from a “load” event. While they’re often not clearly distinguished (e.g. HTML’s onload
and jQuery’s $(document).ready()
considered logically very similar), and both signaling the availability of a resource or processing environment–but they’re not quite the same. Loading (or the finishing of it) is a true event–something signaled from infrastructure to consumer. Readiness, however, is more the rendezvous of the infrastructure’s loading the resource/environment and the consumer’s getting around to being ready to consume/take advantage of that availability. Readiness comes after the load, and is one of those designated special events/coordination points that must be treated as queueable because you can’t capture it any other way. That there are a few very special cases, however, doesn’t mean that every event should be fireable a posteriori.
3