• event loop 与 vue


    结论

    对于event loop 可以抽象成一段简单的代码表示

    
    for (macroTask of macroTaskQueue) {
        // 1. Handle current MACRO-TASK
        handleMacroTask();
          
        // 2. Handle all MICRO-TASK
        for (microTask of microTaskQueue) {
            handleMicroTask(microTask);
        }
    }
    

    js事件机制

    javascript是一个单线程语言,同一时间只能执行一个任务。
    对于javascript的事件处理机制,我们可以简单理解成“主线程+任务队列”模式。主要步骤如下

    (1)所有同步任务都在主线程上执行,形成一个执行栈。

    (2)主线程之外,还存在一个 "任务队列"(task queue)。只要异步任务有了运行结果,就在 "任务队列" 之中放置一个事件。
    (3)一旦 "执行栈" 中的所有同步任务执行完毕,系统就会读取 "任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

    (4)主线程不断重复上面的第三步。

    任务队列

    任务队列分为task queue和microtask queue。执行栈任务清空后会先从Microtasks中取任务,Microtasks中执行完之后才会执行task中的任务。

    因此一个event loop主要流程如下:

    1. 开始一个Event loop
    2. 执行栈从tasks queue中取任务,并执行。
    3. 执行完后,执行栈清空
    4. 执行栈从microtasks queue中取任务执行
    5. 执行完成,执行栈清空
    6. 判断microtasks queue是否还有任务,有则重复步骤3。
    7. 进入 Update the rendering(更新渲染)阶段
    8. Event loop结束

    流程图如下:

    再仔细理解一下开头列出的代码:

    
    for (macroTask of macroTaskQueue) {
        // 1. Handle current MACRO-TASK
        handleMacroTask();
          
        // 2. Handle all MICRO-TASK
        for (microTask of microTaskQueue) {
            handleMicroTask(microTask);
        }
    }
    

    VUE与EVENT_LOOP

    为什么使用Microtask queue

    对于VUE这类web2.0框架而言,最主要做的应该还是把对data的修改映射到DOM上。如果只修改一个数据,就刷新一次DOM,那么在一个同步过程中,同时修改好几个数据,必然会导致多次渲染。这肯定是不可取的。
    从上面的event loop我们了解到,一个event loop对应一次render。理想状况当然就是,一次event loop产生的所有改动最好再render之前将DOM都先更新好。这样在一个周期中就可以只render一次。

    从这点需求出发很容易发现,microtask queue很符合要求。

    1. microtask queue肯定在render ui之前执行完
    2. 不像task queue存在多个,microtask只存在一个queue。
    3. 最关键的是: microtask queue只有都清空了才能进入下一步,无论queue里是什么时候塞进来的。

    实际代码中,不管是鼠标点击还是键盘输入或是网络时间,触发了哪些方法,这些触发都可以看成开启一个event loop。这些触发造成的任何修改都放到microtask queue中,就可以保证在这一轮的evnet loop走到render ui时可以拿到最新的DOM。

    要说明的是,这里的render并不是维护虚拟DOM,也不是把虚拟DOM的变化投射到真实DOM上。而是将真实DOM更新到UI的过程。

    这么说是因为:Event Loop 并不是在 ECMAScript 标准中定义的,而是在 HTML 标准中定义的:

    To coordinate events, user interaction, scripts, rendering, networking, and so forth...

    在 JavaScript Engine 中(以 V8 为例),只是实现了 ECMAScript 标准,而并不关心什么 Event Loop。也就是说 Event Loop 是属于 JavaScript Runtime 的,是由宿主环境提供的(比如浏览器)。
    浏览器可不会关心什么虚拟DOM。只负责DOM改变后渲染UI。

    为什么不用task queue

    同上: 在开启一个event loop后,如果将任务放到task queue中,那么这个task任务只会在本轮Event loop结束后才会执行,并开启新一轮event loop。这无疑会导致两次render UI。
    实际上,尤大为了修复一些bug,曾经将VUE.nexttick用task queue实现。但是导致了很明显的性能问题。
    可以看看两个列子: 例一 , 例二
    两个fiddle的实现一模一样,就是让那个绝对定位的黄色元素起到一个fixed定位的效果:绑定scroll事件,每次滚动的时候,计算当前滚动的位置并更改到那个绝对定位元素的top属性上去。大家自己试试滚动几下,对比下效果,你就会发现第一个fiddle中的黄元素是稳定不动的,fixed很好。而后一个fiddle中就有问题了,黄色元素上下晃动,似乎跟不上我们scroll的节奏,总要慢一点,虽然最后停下滚动时位置是对的。

    上述两个例子其实是在这个issue中找到的,第一个jsfiddle使用的版本是Vue 2.0.0-rc.6,这个版本的nextTick实现是采用了MO,而后因为IOS9.3的WebView里的MO有bug,于是尤雨溪更改了实现,换成了window.postMessage,也就是后一个fiddle所使用的Vue 2.0.0-rc.7。后来尤雨溪了解到window.postMessage是将回调放入的macrotask 队列。这就是问题的根源了。

    参考:

    1. Tasks, microtasks, queues and schedules
    2. 深入理解 JavaScript Event Loop
    3. Vue源码详解之nextTick:MutationObserver只是浮云,microtask才是核心!

    来源:https://segmentfault.com/a/1190000016661045

  • 相关阅读:
    索引自我总结
    面试经典问题---数据库索引B+、B-树
    聚簇索引和非聚簇索引
    SQL2005/2008 无法连接错误
    使用SignalR实现消息提醒
    NHibernate Profiler使用方法
    Mvc model验证总结
    C# Lambda表达式
    ASP.NET MVC Razor
    msysGit管理GitHub代码
  • 原文地址:https://www.cnblogs.com/lovellll/p/10130747.html
Copyright © 2020-2023  润新知