javascript - 说事件发生在我们开始聆听之前是什么意思?

标签 javascript jquery

在阅读 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>标签,或使用 asyncdefer其上的属性。这意味着在短时间内可以单击页面上的任何按钮,但我们尚未在它们上连接处理程序。例如,如果出现临时网络故障,并且需要两秒而不是通常的约 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 线程上运行的事实,看起来不存在竞争条件。但是那里存在竞争条件,因为虽然我们的代码将遵循单线程,但浏览器不是单线程的。所以这可能会发生:

  1. img.src = "path/to/the/image.png"; .
  2. 浏览器的资源管理代码会查找图像。
  3. 资源管理在缓存中找到它,不需要重新验证,因此将图像数据与元素关联并触发 load事件。
  4. 事件系统进入队列任务以回调任何 load元素上存在的事件处理程序找不到任何事件处理程序,并且不会对任何任务进行排队。
  5. 我们附加处理程序并附加元素。
  6. 我们的代码运行至完成。
  7. 我们从来没有得到 loaderror事件回调。

相反,如果我们先钩住,然后设置 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);

然后同样的场景如下所示:

  1. 我们附加我们的处理程序。
  2. img.src = "path/to/the/image.png"; .
  3. 浏览器的资源管理代码会查找图像。
  4. 资源管理在缓存中找到它,不需要重新验证,因此将图像数据与元素关联并触发 load事件。
  5. 事件系统进入队列任务以回调任何 load元素上存在的事件处理程序。它找到了一个,因此它会将一个任务放入队列,以便在任务队列下一次为空时调用它。
  6. 我们追加元素。
  7. 我们的代码运行至完成。
  8. 任务循环从队列中选取下一个任务,这恰好是我们的 load回调。
  9. 任务循环调用我们的回调。

现在,碰巧这种行为非常令人厌烦,现代浏览器不再这样做(尽管 Kaiido says 在 WebKit 上仍然如此,我无法确认)。尽管我仍然像它那样编码(第二个示例),但因为它可以,所以它是浏览器工作的有效方式。

关于javascript - 说事件发生在我们开始聆听之前是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39315372/

相关文章:

jquery - 带有内联可编辑列表的剑道网格

jquery - SVG 悬停过渡

javascript - JSON 按字母顺序排序

jquery - 为点击事件的整个主体创建 jquery 事件

javascript - extjs 按钮范围

javascript - 按数字拆分并将该数字保留在结果中

javascript - 通过基于选择的选择选项获取动态添加 DIV

javascript - 如何在提交按钮点击时调用谷歌验证码

javascript - AJAX 多重响应

javascript - Symfony2 : AJAX request returns full page html