在阅读 Javascript Promises 时,我遇到了一些博客/文章,其中首先将 Promise 实现与 Javascript 事件模型进行比较,然后与回调模式进行比较。
与事件模型进行比较时,通常会提到“事件有可能在我们开始监听之前发生”或“您需要确保所有适当的事件处理程序在事件第一次发生之前添加'
我正在尝试解决这个问题,但从代码的 Angular 来看,我不太确定这如何可能。在我们添加处理程序之前事件如何发生?谁能提供具体例子来解释这一点?
最佳答案
他们的意思几乎就是他们字面意思:事件发生在我们开始监听之前。例如,假设我们显示一个按钮,但在添加监听该按钮点击的事件处理程序之前有两秒的延迟。如果用户在这两秒内单击,则该事件在我们监听之前发生。
示例:
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.
嗯,上面的内容非常通用:考虑将 JavaScript 放入 script
的通常做法。标签位于文档正文的末尾,就在结束之前 </body>
标签,或使用 async
或defer
其上的属性。这意味着在短时间内可以单击页面上的任何按钮,但我们尚未在它们上连接处理程序。例如,如果出现临时网络故障,并且需要两秒而不是通常的约 65 毫秒来加载 JavaScript 文件,那么您的用户将在您挂接处理程序(正在监听它)之前单击按钮(触发事件)。
或者,这曾经是一个很好的例子,但据我所知,现代浏览器不再发生 Kaiido says它在 WebKit 浏览器上仍然如此,尽管我无法在 iOS Safari 上实现它:添加 img
到文档并期望从如下代码中获取加载事件:
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);
从表面上看,由于 JavaScript 的运行到完成语义以及默认情况下我们的 JavaScript 代码在单个 UI 线程上运行的事实,看起来不存在竞争条件。但是那里存在竞争条件,因为虽然我们的代码将遵循单线程,但浏览器不是单线程的。所以这可能会发生:
-
img.src = "path/to/the/image.png";
. - 浏览器的资源管理代码会查找图像。
- 资源管理在缓存中找到它,不需要重新验证,因此将图像数据与元素关联并触发
load
事件。 - 事件系统进入队列任务以回调任何
load
元素上存在的事件处理程序找不到任何事件处理程序,并且不会对任何任务进行排队。 - 我们附加处理程序并附加元素。
- 我们的代码运行至完成。
- 我们从来没有得到
load
或error
事件回调。
相反,如果我们先钩住,然后设置 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);
然后同样的场景如下所示:
- 我们附加我们的处理程序。
-
img.src = "path/to/the/image.png";
. - 浏览器的资源管理代码会查找图像。
- 资源管理在缓存中找到它,不需要重新验证,因此将图像数据与元素关联并触发
load
事件。 - 事件系统进入队列任务以回调任何
load
元素上存在的事件处理程序。它找到了一个,因此它会将一个任务放入队列,以便在任务队列下一次为空时调用它。 - 我们追加元素。
- 我们的代码运行至完成。
- 任务循环从队列中选取下一个任务,这恰好是我们的
load
回调。 - 任务循环调用我们的回调。
现在,碰巧这种行为非常令人厌烦,现代浏览器不再这样做(尽管 Kaiido says 在 WebKit 上仍然如此,我无法确认)。尽管我仍然像它那样编码(第二个示例),但因为它可以,所以它是浏览器工作的有效方式。
关于javascript - 说事件发生在我们开始聆听之前是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39315372/