jiangshanmeta.github.io icon indicating copy to clipboard operation
jiangshanmeta.github.io copied to clipboard

事件冒泡 macrotask microtask

Open jiangshanmeta opened this issue 6 years ago • 1 comments

了解DOM操作,事件冒泡绕不过去 了解Promise,迟早或接触macrotasks和microtasks两个概念

那我为什么要把这几个概念放在一起说?最近读了vue插件vue-clickaway的实现,其中有一段hack性质的代码:

  var initialMacrotaskEnded = false;
  setTimeout(function() {
    initialMacrotaskEnded = true;
  }, 0);

  el[HANDLER] = function(ev) {
    var path = ev.path || (ev.composedPath ? ev.composedPath() : undefined);
    if (initialMacrotaskEnded && (path ? path.indexOf(el) < 0 : !el.contains(ev.target))) {
      return callback.call(vm, ev);
    }
  };

  document.documentElement.addEventListener('click', el[HANDLER], false);

作者说遇到了这么一个问题:这段代码是在microtasks中处理的(promise),换句话说是documentElement监听click事件是在microtasks中执行的,如果触发这段代码执行的是一个普通元素的click事件,会发生先在documentElement上监听click事件,然后由于事件冒泡在documentElement上触发click事件。

这个问题引导我们去想事件冒泡是如何实现的。一个想法是作为一个macrotask,还有一个想法是作为多个macrotask,每当在一个元素上执行完回调,查询是否能冒泡,能冒泡就在macrotasks中添加一个任务。按照上面的描述似乎后者更合理。

为了验证这个想法我们可以做个简单的demo:

<html>
    <head>
        <meta charset="utf-8">
        <title>test</title>

    </head>
    <body>
        <div id="app">
            <button id="button">this is a button</button>
        </div>
        <script>
            var app = document.getElementById("app");
            app.addEventListener("click",function(){
                console.log("app clicked");
            });
            var button = document.getElementById("button");
            button.addEventListener("click",function(){
                console.log("button clicked");
                Promise.resolve().then(function(){
                    console.log("promise")
                });
            });
        </script>
    </body>

</html>

我们会发现顺序是 button=>promise=>app ,如果是冒泡作为一个macrotask结果应该是button=>app=>promise。目前来看事件冒泡是对应多个macrotask。

jiangshanmeta avatar Jun 22 '18 02:06 jiangshanmeta

最后面加上 button.click,如果是冒泡是作为 macrotask 那应该是 button => promise => app,但实际上却是 button => app => promise。链接 vue-clickaway 给的参考链接说:

Previously, this meant that microtasks ran between listener callbacks, but .click() causes the event to dispatch synchronously, so the script that calls .click() is still in the stack between callbacks.

我不太明白这个 synchronously 是啥意思,但事件冒泡应该不是 macrotask 吧。

daolanfler avatar Apr 29 '19 05:04 daolanfler