What Is Meant By Saying That Events Happened Before We Started Listening To Them?
Solution 1:
They mean pretty much what they literally said: The event occurred before we started listening for it. For instance, suppose we show a button, but there's a delay of two seconds before we add an event handler listening for clicks on that button. If the user clicks within those two seconds, the event occurs before we were listening for it.
Example:
function run() {
// Show a button
document.getElementById("btn").style.display = "inline";
// Wait two seconds before we hook up a handler
setTimeout(function() {
document.getElementById("btn").addEventListener("click", function() {
console.log("Got a click");
}, false);
}, 2000);
// If the user clicks within those two seconds, the event happened
// before we were listening for it.
}
// This is just stuff to make sure the user is ready before showing the button
var counter = 4;
tick();
function tick() {
if (counter == 0) {
document.getElementById("get-ready").style.display = "none";
run();
} else {
document.getElementById("countdown").innerHTML = counter--;
setTimeout(tick, 1000);
}
}
<p id="get-ready">Get ready, a button will appear in <span id="countdown"></span>, click it as soon as it appears, then again a couple of seconds later:</p>
<input style="display: none" type="button" id="btn" value="Click me as soon as I appear, and again a couple of seconds later">
What I was looking for was some generic cases where events are fired before we attach listeners.
Well, the above is quite generic: Consider the usual practice of putting your JavaScript in a script
tag at the end of the document body, just before the closing </body>
tag, or using the async
or defer
attributes on it. That means there is a brief period of time when any buttons on the page can be clicked, but we haven't hooked up handlers on them yet. If there's a temporary network glitch, for instance, and it takes two seconds instead of the usual ~65ms to load your JavaScript file, your users would click the button (trigger the event) before you hooked your handler (were listening for it).
Alternately, this used to be a good example, but no longer happens on modern browsers as far as I can tell Kaiido says it still does on WebKit browsers, although I couldn't make it happen on iOS Safari: Adding an img
to a document and expecting to get the load event from code like this:
var img = document.createElement("img");
img.src = "path/to/the/image.png";
img.addEventListener("load", function() {
// Handle the fact it loaded
}, false);
img.addEventListener("error", function() {
// Handle the fact it failed
}, false);
someOtherElement.appendChild(img);
On the surface, it looks like there's no race condition there because of JavaScript's run-to-completion semantics and the fact that by default our JavaScript code runs on a single UI thread. But there is a race condition there, because while our code will follow a single thread, the browser is not single-threaded. So this can happen:
img.src = "path/to/the/image.png";
.- The browser's resource management code goes looking for the image.
- Resource management finds it in cache and doesn't need to re-verify, so associates the image data with the element and triggers the
load
event. - The event system goes to queue tasks for callbacks to any
load
event handlers that are present on the element, doesn't find any, and doesn't queue any tasks. - We attach our handlers and append the element.
- Our code runs to completion.
- We never get either a
load
orerror
event callback.
In contrast, if we hook first and then set src
:
var img = document.createElement("img");
img.addEventListener("load", function() {
// Handle the fact it loaded
}, false);
img.addEventListener("error", function() {
// Handle the fact it failed
}, false);
img.src = "path/to/the/image.png"; // <=== Moved
someOtherElement.appendChild(img);
Then the same scenario plays out like this:
- We attach our handlers.
img.src = "path/to/the/image.png";
.- The browser's resource management code goes looking for the image.
- Resource management finds it in cache and doesn't need to re-verify, so associates the image data with the element and triggers the
load
event. - The event system goes to queue tasks for callbacks to any
load
event handlers that are present on the element. It finds one, so it queues a task to call it when the task queue is next empty. - We append the element.
- Our code runs to completion.
- The task loop picks up the next task from the queue, which happens to be our
load
callback. - The task loop calls our callback.
Now, it happens that this behavior was sufficiently irksome that modern browsers don't do it anymore (although Kaiido says it still does on WebKit, I can't confirm that). Although I still code as though it does (the second example), because it could, it's a valid way for the browser to work.
Post a Comment for "What Is Meant By Saying That Events Happened Before We Started Listening To Them?"