美文网首页
浏览器的Event loop

浏览器的Event loop

作者: 吴晗君 | 来源:发表于2019-04-10 23:21 被阅读0次

    js为什么是单线程的?

    JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

    所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

    为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

    什么是 event-loop

    event loop是一种运行机制,在Javascript中用来解决单线程疲于应对多任务的问题。event loop在不同的环境中有不同的实现。如Node、各浏览器。
    [图片上传失败...(image-100eb7-1554909685850)]

    什么是宏任务和微任务

    task的定义

    An event loop has one or more task queues. A task queue is an ordered list of tasks, which are algorithms that are responsible for such work as:EventsParsingCallbacksUsing a resourceReacting to DOM manipulation
    也就是说task是浏览器(或者当前运行环境)去执行Js、操作dom的一个个任务。事件触发的时候,触发的回调就会被推入这个队列中。而event loop会不断循环去获取当前task队列里的第一项拿到栈中执行。

    我举个例子:我在页面上绑定了一个点击事件onClick,并且绑定了一个回调事件,这就是一个简单的发布订阅。当我的鼠标点击,触发这个事件的时候,dispatch Click这个task就会放到task queue中,而onClick回调就会被放到执行栈中执行。onClick执行完了就完成了一次event loop。浏览器(或当前运行环境)会不断去队列里取task来执行。

    microtask的定义

    Each event loop has a microtask queue. A microtask is a task that is originally to be queued on the microtask queue rather than a task queue.

    也就是说它也是一种task,不过microtask独立有一个队列。不过它的特点是:

    1. 在上一个task完成之后,下一个task之前执行,
    2. 并且当前栈必须为空。
    3. task一样,microtask是有顺序的,不会因为后面加入的优先级高而先执行。
    4. 最后,当这个microtask执行完了,当前task才算执行完成。

    microtask的用途是什么?

    其中一个是减少渲染。根据HTML Standard,在每个 task 运行完以后,UI 可能会重渲染,那么比如Vue等框架在 microtask 中就完成数据更新,当前 task 结束就可以得到最新的 UI 了。反之如果新建一个 task 来做数据更新,那么渲染就会进行两次。

    代码里常用的microtasktask有哪些?

    task: setImmediate > messageChanel > setTimeout

    microtask: then > MutationObserver

    Vue.nextTick源码中有依次降级用到then、MutationObserver、setImediate、setTimeout

    MessageChannel的用法

    // ie10以上支持
    // iframe中有用到
    let channel = new MessageChannel();
    let port1 = channel.port1;
    let port2 = channel.port2;
    console.log(port1)
    console.log(port2)
    
    // vue.nextTick 以前用这个方式实现的,废弃了
    port1.onmessage = function(e){
        console.log(e.data)
    };
    port2.postMessage('你好');
    // vue使用nextTick历史:2.5之前用micro task,2.5用macro task,2.5之后用micro task
    // vue 在2.5使用macro task(也就是MessageChannel)的原因是 micro task拥有过高的优先级,
    // 导致了一些bug(#4521, #6690, #6566)。setImmediate被作者任务是完美的,但只有ie和node支持
    // 2.5之后改回来micro task的原因macro task引发了一些无法绕过的bug  #7109, #7153, #7546, #7834, #8109
    

    MutationObserver的用法

    // ie11以上支持
    // mutationObserver是个微任务,提供了监视DOM树更改的能力
    // vue 等待dom更新后 在取dom中的数据
    setTimeout(()=>{
        console.log('timeout');
    })
    let mutationObserver = new MutationObserver((...args)=>{
         console.log(args)
         console.log(app.children.length); // 异步的
    })
    mutationObserver.observe(app, {childList:true})
         for(let i = 0 ; i< 10; i++){
         app.appendChild(document.createElement('p'))
    }
    

    有缘人看到这篇文章可以看一下下面参考里的第一篇文章,通读两遍,做一下里面的几个练习题,有非常详细的思路讲解,再结合HTML规范,我觉得就可以弄清楚Event loop,micro task就会用了,那些代码我就不照搬了。

    参考

    相关文章

      网友评论

          本文标题:浏览器的Event loop

          本文链接:https://www.haomeiwen.com/subject/vjmeiqtx.html