Javascript 为什么以编程方式单击按钮与单击DOM时,任务/微任务的执行顺序会有所不同?

Javascript 为什么以编程方式单击按钮与单击DOM时,任务/微任务的执行顺序会有所不同?,javascript,Javascript,在DOM中单击按钮与通过编程单击按钮时,微任务/任务队列的执行顺序有所不同 const btn=document.querySelector('#btn'); btn.addEventListener(“单击”,函数(){ Promise.resolve().then(函数(){console.log('resolved-1');}); console.log('click-1'); }); btn.addEventListener(“单击”,函数(){ Promise.resolve().t

在DOM中单击按钮与通过编程单击按钮时,微任务/任务队列的执行顺序有所不同

const btn=document.querySelector('#btn');
btn.addEventListener(“单击”,函数(){
Promise.resolve().then(函数(){console.log('resolved-1');});
console.log('click-1');
});
btn.addEventListener(“单击”,函数(){
Promise.resolve().then(函数(){console.log('resolved-2');});
console.log('click-2');
});
点击我

与“本机”事件不同,本机事件由DOM和invoke事件触发 dispatchEvent通过事件循环异步调用处理程序 同步执行事件处理程序。所有适用的事件处理程序都将 在调用之后代码继续之前执行并返回 调度事件

dispatchEvent是创建初始分派过程的最后一步, 用于将事件分派到实现的事件中 模型可以使用事件构造函数创建事件

有趣的问题

首先,简单的部分是:当您调用
单击
时,它是一个同步调用,触发按钮上的所有事件处理程序。您可以看到,如果在呼叫周围添加日志记录:

const btn=document.querySelector('#btn');
btn.addEventListener(“单击”,函数(){
Promise.resolve().then(函数(){console.log('resolved-1');});
console.log('click-1');
});
btn.addEventListener(“单击”,函数(){
Promise.resolve().then(函数(){console.log('resolved-2');});
console.log('click-2');
});
document.getElementById(“btn模拟”).addEventListener(“单击”,函数(){
log(“即将调用单击”);
点击();
log(“完成调用单击”);
});

所以,Chrome的回答仅仅是因为它有趣(关于一般的DOM回答,请参阅T.J Crowder的优秀回答)

调用<>代码> HTMLMENT::C++/COM> C++,它是DOMNETRES:/P>的对应部分
void HTMLElement::click(){
DispatchSimulatedClick(nullptr、kSendNoEvents、,
模拟ClickCreationScope::kFromScript);
}
它主要围绕DispatchMouseeEvent进行一些工作,并处理边缘案例:

void EventDispatcher::DispatchSimulatedClick(
节点&节点,
事件*基础事件,
模拟单击鼠标事件选项鼠标事件选项,
模拟点击创建范围创建(U范围){
//此持久性向量不会导致泄漏,因为添加的节点将被删除
//在dispatchSimulatedClick()返回之前。此向量仅用于
//防止代码运行到
//dispatchSimulatedClick()。
定义\u静态\u本地(持久,
节点\u调度\u模拟\u点击,
(MakeGarbageCollected());
if(IsDisabledFormControl(&node))
返回;
if(节点\调度\模拟\单击->包含(&node))
返回;
节点\u调度\u模拟\u单击->插入(&节点);
if(鼠标事件选项==kSendMouseOverUpDownEvents)
EventDispatcher(节点,*MouseeEvent::Create(事件类型名称::kMouseover),
node.GetDocument().domWindow(),
基础(事件、创建(范围))
.Dispatch();
if(鼠标事件选项!=kSendNoEvents){
EventDispatcher(节点,*MouseeEvent::Create(事件类型名称::kMousedown,
node.GetDocument().domWindow(),
基础(事件、创建(范围))
.Dispatch();
node.SetActive(true);
EventDispatcher(节点,*MouseeEvent::Create(事件类型名称::kMouseup,
node.GetDocument().domWindow(),
基础(事件、创建(范围))
.Dispatch();
}
//某些元素(例如颜色选择器)可能会在之前将活动状态设置为true
//调用此方法并期望在调用期间重置状态。
node.SetActive(false);
//始终发送单击
EventDispatcher(节点,*MouseEvent::Create(事件类型名称::kClick,
node.GetDocument().domWindow(),
基础(事件、创建(范围))
.Dispatch();
节点\u调度\u模拟\u单击->擦除(&节点);
}
它在设计上是完全同步的,以使测试变得简单,同时也出于遗留原因(想想奇怪的事情)


这只是一个直接呼叫,不涉及任务调度。EventTarget通常是一个同步接口,它不会延迟事情,它早于microtick语义并承诺:

这是它的一半。:-)@T.J.Crowder我是你的粉丝:D我喜欢你的综合答案
按钮。点击()
实际上并不是点击按钮来检查我的理解。当我执行
.click()
时,就好像我在执行
handler1();handler2()
完成,然后执行微任务?实际上,
单击
调度事件
之间有细微的区别(我们在Testim上玩东西时总是遇到这些问题,必须正常化)。我会看看今天晚些时候是否能找到chromium代码进行讨论。另外-请注意,
.click
是特殊的,而“它为什么会这样工作”的答案是“遗留的,因为受信任的事件并不总是一件事”
。click
在DOM中非常特殊(更不用说DOMActivate fun了)。我会给出一个要点,但作为一个简单的要点,每个
。click()
都会(匿名)向home base(谷歌)报告在Chrome中(通过RuntimeCallStatCounter),Chrome和dispatchEvent不会:D@BenjaminGruenbaum-呃,连t这样的匿名电话家庭都不喜欢
btn.click();