在处理当前窗体上的所有事件后运行的排队函数

Enqueuing function to run after all events on current form are handled

提问人:Niklas Mohrin 提问时间:3/15/2022 更新时间:3/15/2022 访问量:212

问:

我有一个带有事件处理程序的表单,如果在 DOM 中找到某个状态,该处理程序会更新一个字段。表单提交后,需要更改 DOM,但事件处理程序仍应遵守旧状态。目前,我通过在同一窗体上使用事件处理程序来实现这一点,因为这会将函数排入队列以稍后运行 - 希望在处理事件之后。我的问题是:这种行为是否得到保证,如果没有,是否有一种规范支持的方式来实现这种行为?formdataFormDataformdatasetTimeout(() => {/*...*/}, 0)submitformdata

事件循环的规范中,选择下一步要执行的工作的第一步描述为:

让 taskQueue 成为事件循环的任务队列之一,以实现定义的方式选择 [...]

然后,稍后将运行此所选队列中最早的任务。这意味着,如果调度的函数与事件处理程序位于同一任务队列中,并且如果事件处理程序是在提交处理程序实际运行之前调度的,那么我肯定是安全的 - 但我找不到任何证据。从 formdata 事件的文档(“在构造表示表单数据的条目列表后触发”)以及处理程序在处理程序之后运行的事实来看,我什至会假设相反的情况是正确的 - 但这不是我在上述方法中观察到的。setTimeoutformdataformdatasubmit

JavaScript 事件循环 Web 标准

评论


答:

2赞 Kaiido 3/15/2022 #1

你的理解是非常正确的,你是对的,可能并不总是在“相关”事件之后触发:这些事件确实很有可能是从两个不同的任务触发的,它们不太可能使用计时器任务源,并且你正确地识别了使事件循环“可能”从其他任务源中选择任务的位。setTimeout(fn, 0)

但是,在这种情况下,您是安全的

事件和事件是从同一个“任务”*触发的。submitformdata

如果查看表单提交算法,可以看到 submit 事件是在步骤 6.5 中直接触发的,而不是像通常那样被包装在排队的任务中。

shouldContinue 是触发在表单中命名的事件的结果 [...]submit

然后在同一算法中,没有任何并行或任何暗示异步性的东西,我们有步骤 8 说

让条目列表是使用表单提交者和编码构建条目列表的结果。

在这个构建条目列表算法时,在第 7 步中,我们调用

触发以表单命名的事件 [...]formdata

再一次,不允许任何异步性。

因此,我们可以确定这些事件将在两者之间没有任何其他内容(除了微任务)的情况下触发,并且来自事件的计时器回调将在回调后触发,即使是在同一帧中调度的两个回调(对于事件循环也是“同步的”)也无法在那里交错:submitformdatarequestAnimationFrame

document.forms[0].addEventListener("submit", e => {
  console.log("submit");
});
document.forms[0].addEventListener("formdata", e => {
  console.log("formdata");
});
requestAnimationFrame(() => {
  console.log("rAF 1");
  document.forms[0].querySelector("button").click();
});
requestAnimationFrame(() => {
  console.log("rAF 2");
});
<form target="target">
  <input value="foo" name="bar">
  <button>
  submit
  </button>
</form>
<iframe name="target"></iframe>

*从技术上讲,它甚至不需要来自任务本身,您可以很好地强制这些事件从微任务或调整大小事件回调等触发。

评论

0赞 Niklas Mohrin 3/15/2022
太好了,我不知道调度(如偶数或“触发事件”的文本)意味着同步运行事物,并认为“调度”总是意味着推送到队列(我现在也阅读了 mdn 中的正确段落)。需要明确的是,提交事件是从队列开始还是直接触发并不重要,因为所有操作仅在提交开始时才开始,对吧?无论如何,正是我想要的,非常感谢!dispatchEventdispatchEvent
0赞 Kaiido 3/15/2022
是的,整个提交是作为一个块执行的,所以它确实无关紧要,它是从哪里开始的。