视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
JS内部事件机制之单线程原理
2020-11-27 22:12:09 责编:小采
文档


任务队列

主线程:正在执行的代码,会生成函数调用栈。

  • macro-task(宏任务,新名:task)包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
  • micro-task(微任务,新名:jobs)包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性,队列中只能有一个)
  • 任务分类

    同步任务,语句只按语句先后顺序执行,前面未执行完,不会执行后面语句。

    异步任务,语句不在语句先后顺序上执行,执行到该代码时,加入到相应任务队列,延后执行。

    单线程

    主线程从 script (整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的 jobs。当所有可执行的 jobs 执行完毕之后。循环再次从 task 开始,找到其中一个任务队列执行完毕,然后再执行所有的 jobs,这样一直循环下去。

    注意事项

  • setTimeout 最小间隔不能低于 4 毫秒,否则会自动增加。
  • DOM 的渲染每 16 毫秒执行一次,因为显示器是 60 Hz,16ms 刷新一次。
  • process.nextTick 任务会在 jobs 里单独维护一个队列,并且在其他 jobs 任务之前执行。
  • 冒泡事件会直接在子元素事件执行完成后,插入在主线程中。如果主线程不为空,那么会优先于 jobs 执行。
  • 经典示例

    示例详解:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

    通过鼠标点击

    <div class="outer">
     <div class="inner"></div>
    </div>
    // Let's get hold of those elements
    var outer = document.querySelector('.outer');
    var inner = document.querySelector('.inner');
    // Let's listen for attribute changes on the
    // outer element
    new MutationObserver(function() {
     console.log('mutate');
    }).observe(outer, {
     attributes: true
    });
    // Here's a click listener…
    function onClick() {
     console.log('click');
     setTimeout(function() {
     console.log('timeout');
     }, 0);
     Promise.resolve().then(function() {
     console.log('promise');
     });
     outer.setAttribute('data-random', Math.random());
    }
    // …which we'll attach to both elements
    inner.addEventListener('click', onClick);
    outer.addEventListener('click', onClick);
    // 
    输出结果 click mutate click mutate promise promise timeout timeout

    进阶--通过js执行

    <div class="outer">
     <div class="inner"></div>
    </div>
    // Let's get hold of those elements
    var outer = document.querySelector('.outer');
    var inner = document.querySelector('.inner');
    // Let's listen for attribute changes on the
    // outer element
    new MutationObserver(function() {
     console.log('mutate');
    }).observe(outer, {
     attributes: true
    });
    // Here's a click listener…
    function onClick() {
     console.log('click');
     setTimeout(function() {
     console.log('timeout');
     }, 0);
     Promise.resolve().then(function() {
     console.log('promise');
     });
     outer.setAttribute('data-random', Math.random());
    }
    // …which we'll attach to both elements
    inner.addEventListener('click', onClick);
    outer.addEventListener('click', onClick);
    inner.click();
    // 
    输出结果 click click mutate promise promise timeout timeout

    由于点击事件是 js 执行的,inner 的 onClick 函数执行完成时,inner.click() 语句的作用域还没有退栈,主线程调用栈不是空的,导致 jobs 队列任务不会执行,mutate 和 promise 语句都未能在事件循环中执行到。从而执行了 outer 的 onClick 函数。outer 的 onClick 函数执行完成后,inner.click() 语句才退栈,继而执行 jobs 的任务。

    只有一个 mutate 是由于 jobs 队列中,只能有一个 MutationObserver 任务,第二次创建时,前一个 MutationObserver 任务没有执行,顾不再创建。

    总结

    以上所述是小编给大家介绍的JS内部事件机制之单线程原理,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

    下载本文
    显示全文
    专题