点击处理程序内的Promise.resolve()

时间:2020-05-29 14:33:36

标签: javascript dom

我正在尝试为某些事件处理程序创建一些装饰器:一些简单的函数,将处理程序 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触发时,该处理程序会等待诺言,因此添加的第二个处理程序会在第一个处理程序之前触发。

1 个答案:

答案 0 :(得分:1)

区别在于,在setTimeout情况下,您要在preventDefault()版的Promise中调用async回调中的await。当您等待Promise时,事件将结束,此后将无法再通过preventDefault()进行拦截。