在处理当前表单上的所有事件后,将函数排队到 运行

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

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

specification of event loops中,选择下一步工作的第一步描述为:

Let taskQueue be one of the event loop's task queues, chosen in an implementation-defined manner [...]

此所选队列中最旧的任务随后 运行。这意味着,如果使用 setTimeout 调度的函数与事件处理程序在同一任务队列中,并且如果 formdata 事件处理程序在提交处理程序实际上是 运行 之前调度,我会绝对安全——不过我找不到任何证据。从 documentation of the formdata event(“在构建表示表单数据的条目列表之后触发”)以及 formdata 处理程序在 submit 处理程序之后的 运行 这一事实,我甚至会假设相反的情况是真实的——但这不是我用上述方法观察到的结果。

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

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

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

如果你看表​​格 submit algorithm, you can see that the submit event is directly fired at step 6.5, instead of being wrapped in a task that would get queued 就像经常这样。

Let shouldContinue be the result of firing an event named submit at form [...]

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

Let entry list be the result of constructing the entry list with form, submitter, and encoding.

在这个 constructing the entry list 算法中,在第 7 步,我们调用了

Fire an event named formdata at form [...]

再次不允许任何异步性。

所以我们可以确定这些事件会触发,中间没有任何其他事件(微任务除外),并且 submit 事件的定时器回调会在 formdata 回调之后触发,即使在同一帧中安排的两个 requestAnimationFrame 回调(对于事件循环也是“同步”)将无法在那里交错:

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>

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