我正在尝试为某些事件处理程序创建一些装饰器:一些简单的函数,将处理程序 A 作为参数,并返回执行某些操作的函数 B 然后致电 A(...arguments)
。
我特别想构建一个装饰器,它应该在所有其他处理程序之后触发作为参数传递的处理程序。但是我注意到,如果事件是通过编程方式而不是手动方式触发的,则点击次数处理会有所不同。
在<a href="some/path">bla</a>
标签的情况下尤其明显,在 click
事件上带有异步处理程序,该处理程序等待下一个线程完成执行(即在某个时候具有 await Promise.resolve()
)。
例如给出此代码的
<a href="">click</a>
<script>
const a = document.querySelector('a')
a.addEventListener('click', fireLastDecorator(handler))
a.addEventListener('click', () => console.log('doing stuff...'))
setTimeout(click, 10000)
function handler(e) {
e.preventDefault()
console.log('done')
}
function fireLastDecorator(f) {
return async function() {
await Promise.resolve()
f.apply(this, arguments)
}
}
function click() {
a.click()
}
</script>
在10秒的超时期间,我可以随时单击链接上的任意位置:页面不会重新加载,因为e.preventDefault()
语句即使有 {{1}也会阻止链接的加载} 。
但是,当setTimeout触发其await Promise.resolve()
函数(以编程方式)时,只要程序等待 click
,页面就会开始重新加载自身。该函数在等待之后继续执行(如果您激活了持久日志,则可以在控制台上看到'done'字符串打印),但是无论如何页面都会重新加载。
我想知道为什么这种行为与众不同。
这在输入类型提交时也很明显。
编辑
在调试器中,我注意到当手动而不是通过js触发单击时,脚本会遵循不同的路径:手动触发时,处理程序不会等待Promise的解决,因此第二个处理程序在触发之后第一个(好像Promise.resolve()被忽略)。通过js触发时,该处理程序会等待诺言,因此添加的第二个处理程序会在第一个处理程序之前触发。
答案 0 :(得分:1)
区别在于,在setTimeout
情况下,您要在preventDefault()
版的Promise中调用async
回调中的await
。当您等待Promise时,事件将结束,此后将无法再通过preventDefault()
进行拦截。