关于JavaScript操作DOM是同步还是异步
js常见能够改变执行队列的`Promise、setTimeout、`标签等等都没有在这里出现,所以确认是同步执行无疑。 既然同步执行为什么

最近公司项目大多数还停留在jQuery的年代,所以难免不了去操作DOM,发此记录一下关于JS操作DOM
操作DOM的栗子
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
var body = document.querySelector('body');
console.log('羊先生');
var cDiv = document.createElement('div');
console.log(cDiv)
console.log('vipbic');
body.appendChild(cDiv)
console.log(body);
</script>
</body>
</html>
以上结果目前和我们预想的结果是一致的,自上而下依次同步执行,这里划重点,js引擎线程
增加阻塞
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<style>
.easy {
width: 200px;
height: 200px;
background: pink;
}
.hard {
background: yellow;
transition: 2s all;
}
</style>
<script>
var body = document.querySelector('body');
console.log('羊先生');
var cDiv = document.createElement('div');
console.log(cDiv)
console.log('vipbic');
body.appendChild(cDiv)
console.log(body);
cDiv.classList.add('easy')
for(var i = 0;i<300000000;i++){
}
cDiv.classList.add('hard')
console.log(cDiv)
</script>
</body>
</html>
classList
html5新增API,classList 属性返回元素的类名,作为 DOMTokenList 对象,在线列子
效果如下

可以看到,刷新浏览器,理论上在阻塞的情况下
关于浏览器渲染进程多线程
GUI渲染线程
负责渲染浏览器界面,解析HTML、CSS、构建DOM树和RenderObject树,布局和绘制等
当界面需要重绘(Repaint)或由于某种操作导致重排(Reflow)时,该线程就会执行
GUI渲染线程和JS引擎线程是互斥的,当js引擎执行时GUI线程会被挂起(相当于冻结),GUI更新会被保存在一个队列中等到js引擎空闲时立即执行
JS引擎线程
也称之为js内核,负责处理javascript脚本程序(V8引擎)
JS引擎线程负责解析javascript脚本,运行代码
JS引擎一直在等待着任务的到来,然后加以处理,一个renderer进程中无论如何都只有一个js线程在运行js程序
由于GUI渲染线程和JS引擎线程是互斥的,所以如果JS执行时间如果过长,这样就会造成页面渲染不连贯,导致页面渲染加载阻塞
事件触发线程
归属于浏览器而不是js引擎,用来控制事件循环
当js引擎执行代码,如setTimeout时(也可以是来自浏览器内核的其他线程,如鼠标点击,ajax请求等),会将对应的任务添加到事件线程当中
当对应的事件符合触发条件被触发时,事件线程会把事件添加到待处理事件队列的队尾,等待js引擎的处理
由于js的单线程关系,所以这些待处理队列中的事件都得排队等待js引擎处理(当js引擎空闲时才会去执行)
定时器触发器线程
setInterval和setTimeout所在的线程
浏览器定时计数器并不是由js引擎计数的(因为js是单线程的,如果处于阻塞状态就会影响计数的准确性)
单独的线程来计时并触发定时(计时完毕后,添加到事件队列中,等待js引擎空闲后执行)
W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms
异步http请求线程
在XMLHttpRequest在连接后通过浏览器开一个线程请求
将检测到状态变更时,如果有设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中,再有js引擎执行
这里说的是主流的浏览器渲染进程的主要几个线程,但不囊括所有,如果遇到相应的问题,欢迎评论。
总结
js常见能够改变执行队列的Promise、setTimeout、<script>标签等等都没有在这里出现,所以确认是同步执行无疑。
既然同步执行为什么会有"异步"的效果,这里要说到上文划重点内容: js引擎线程与GUI渲染线程。也就是说,js引擎线程与GUI渲染线程互斥,这是线程之间的"同步"造成的操作DOM时的"异步"效果。
<div>的样式为什么没有生效呢?明明有一个过渡效果。原因是:浏览器的渲染时会执行优化策略,即将多个同一DOM下的样式合并后渲染
虽然DOM操作是同步的,而你期望的「DOM操作」却不一定是同步的,包括调用外设(外设要看具体设备,有的设备会阻塞浏览器执行,什么意思,就是浏览器的异步操作也会停止,结合这里的异步操作的理解,就可以解释一些奇怪现象了)等,需要在日常开发的时候注意