关于修改DOM是异步还是同步的问题

6

@bf 同学

本篇文章不是笔记也不是心得,而是关于一个问题的讨论,问题最初出现于https://segmentfault.com/q/1010000005630545?_ea=903562

由于 @bf 同学不方便加QQ/微信,而这个问题又比较大,在问答评论里不好描述清楚,so,趁着周末专门写了一篇文章来回应 @bf 同学

@bf 同学,提到了一个观点:对DOM的修改永远是异步的

当时就震惊到我了(可能技术不达标,少见多怪的缘故,哈哈)

说实话,干了好几年开发,第一次明确地听到有人这样说,根据自己看的书及一些javascript编程经验来说,起初我认为是错误的。

然后看了看 @bf 同学的回复,能自圆其说,还说的头头是道,所以我真的以为对DOM的修改永远是异步的是正确的。然后怀着震惊的心情(因为跟经验相违背),还写了一篇博客记录http://www.liyanshan.com/2016/06/09/%E5%AF%B9DOM%E7%9A%84%E4%BF%AE%E6%94%B9%E6%B0%B8%E8%BF%9C%E9%83%BD%E6%98%AF%E5%BC%82%E6%AD%A5%E7%9A%84/

经过这么些天的发酵和消化,觉得对这个观点又回到了最初的认识(即这个观点是错的)。

还请 @bf 同学跟我多多探讨 !

关于javascript修改dom是异步还是同步的问题:

先来看代码:

<ul>
    <li id="i0"></li>
    <li id="i1"></li>
    <li id="i2"></li>
    <li id="i3"></li>
    <li id="i4"></li>
</ul>
<ul id="newEle"></ul>

<script>
 for(var i = 0;i<5;i++){
    var item = document.getElementById('i'+i);
    item.innerHTML = i;
 }
 var newEle = document.getElementById('newEle');
 for(i=0;i<5;i++){
    var li = document.createElement("li");
    li.innerHTML = i;
    newEle.appendChild(li);
 }
</script>  

上述代码的结果完全就是同步的表现,如果是异步的话,毫无疑问,第一个ul下的li每个内容都应该是5,第二个也应该是5。

这是数学中的反证法。即一个命题,哪怕我找出一个特例(何况我能找出很多例子)能推翻这个命题,那么这个命题就不成立。

@bf 同学可能会说了,他也用反证法,比如script标签的加载,来证明DOM修改是异步的

但是这个特例的问题在于:

把下载的异步性当成了DOM修改的异步性

script标签加载是异步的,因为要走网络(比如走网络的ajax和图片下载等都是异步的,当然ajax也可以写成同步的),也就是说,浏览器开了一个线程下载要用的script,但是马上返回(交给HTTP请求线程就不管了,请求结束,请求线程会把结果放入事件队列里),接着执行或下载其他部分。其实这个问题我在引起这个讨论的问题上已经回答了(可能回答的没有那么清楚)。

然后又陆陆续续地看了一些书,查了一些资料,问了一些大牛,越来越坚信DOM修改是同步的

JavaScript异步编程第一章 - 异步的I/O函数(1.2.1)

图片描述

这个也符合我最初跟 @bf 同学的解释:修改DOM是同步的,但是渲染是异步的。因为JavaScript引擎线程跟GUI渲染线程是互斥的,即我执行的时候,你就靠边站,我执行完你才能执行

详见我2014年写的一篇关于异步的文章(当时就是记录一下心得与笔记,也不会自己搭博客,也不会MD,所以请 @bf 同学凑合看)http://blog.sina.com.cn/s/blog_6fd55a970102v64x.html

在群里提问

这个其实就相当于回答了js是同步修改的。。因为这是三个异步函数!

图片描述

@bf 同学在后面的回复中,提到了一篇文章https://leozdgao.me/why-dom-slow/,额,我看了看这篇文章,发现其实我今年年初就看过了,当时貌似是用浏览器重排和重绘搜索到的。。

这篇文章主要是讲 重排和重绘及性能优化一类的知识。

@bf 同学可能受到这句话的影响:

一般情况下,浏览器的layout是lazy的,也就是说:在js脚本执行时,是不会去更新DOM的,任何对DOM的修改都会被暂存在一个队列中,在当前js的执行上下文完成执行后,会根据这个队列中的修改,进行一次layout。

这个其实说明不了修改DOM永远是异步的,这个是JavaScript引擎实现层面上的知识,是对js修改DOM的优化,它放入的队列其实不是事件队列,如果放到了事件队列中,才是异步的。。C++实现一个队列是何其简单啊!js实现队列更简单!只是实现层面的东西,对程序猿都是透明的。。所以没啥可说的。只能帮助我们理解重排和重绘的机制,而不能得出修改DOM永远是异步的结论。。

你可能感兴趣的

bf · 2016-06-26

所以修改 DOM 永远是异步地(生效)的。而 DOM 结构本身当然是被同步地修改的。

+1 回复

0

赞,当我看到文章的第一段代码时就感觉作者理解错了

胡文飞 · 2018-04-14
flcwl · 7月27日

https://www.zhihu.com/questio...
你能回答这个问题,你就懂了。。

+1 回复

bf · 2016-06-26

DOM 是什么?DOM 是 Javascript 和引擎内部维护的 HTML 结构的沟通机制(接口)。异步是什么?异步是不等待结果直接返回。修改 DOM 异步是什么?修改 DOM 异步意味着对 DOM 的修改是异步地反映在 浏览器内部维护的 HTML 结构上的。没有人说 修改 DOM 对 DOM 而言是异步的。修改 DOM 对浏览器内部的 HTML 结构是异步的。请不要脱离原问题理解这句话。原问题的语境是修改 DOM 的实际效果,而非 DOM 这个作为沟通的抽象的结构。

回复

bf · 2016-06-26

https://leozdgao.me/why-dom-slow/ 的语用看,修改 DOM 有两个含义——1 达到修改 DOM 的目的(修改内部 HTML 结构);2 修改 DOM 借口本身的内容。若解释为 1,则 修改 DOM 绝对永远是异步的,除非浏览器内核是你自己编写的;若解释为 2,则修改 DOM 绝对永远是同步的,除非借助自己实现的异步调用机制(如 通过任何形式的 nextTick 实现,如 setImmediate, Promise, MutationObserver, requestAnimationFrame, setTimeout)。

回复

bf · 2016-06-26

问题已结。

回复

bf · 2016-06-26

回复

darr250 · 2016-09-01

hi,我想请教下,为什么说“渲染是异步的”呢?

回复

cainankun · 2017-09-14

咦,你这是在哪个群里面有司徒正美呀?方便拉我进群么?

回复

载入中...