JS为什么是单线程的,多线程不香么?
原因是,并行操作可能会使系统对dom节点的改动发生冲突(可以参考操作系统的互斥部分)。比如同时对一个dom节点进行增加和删除,那该怎么办呢?没法干,容易出事 为了利用多核CPU的计算能力,h5提出web worker标准,允许js脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。新标准没有改变js单线程的本质。
同步任务与异步任务
同步任务,顾名思义,就是立即执行的任务,同步任务一般会直接进入到主线程中执行;而异步任务,就是异步执行的任务,比如ajax网络请求,setTimeout 定时函数等都属于异步任务,异步任务会通过任务队列( Event Queue )的机制来进行协调。事件循环呢,其实就是在同步任务与异步任务的交替执行中形成的。
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
试着去理解一下,这个答案是 script start、script end 、promsie1、promise2、setTimeout 同步和异步任务分别进入不同的执行环境,同步的进入主线程,即主执行栈,异步的进入 Event Queue 。主线程内的任务执行完毕为空,会去 Event Queue 读取对应的任务,推入主线程执行。 上述过程的不断重复就是我们说的 Event Loop (事件循环)。 在事件循环中,每进行一次循环操作称为tick,通过阅读规范可知,每一次 tick 的任务处理模型是比较复杂的,其关键的步骤可以总结如下: 在此次 tick 中选择最先进入队列的任务( oldest task ),如果有则执行(一次) 检查是否存在 Microtasks ,如果存在则不停地执行,直至清空Microtask Queue 更新 render 主线程重复执行上述步骤 // 具体的运行方法需要从宏任务和微任务的角度去理解 https://www.cnblogs.com/yugege/p/9598265.html(原文)
减少dom操作,代价大、耗性能
DOM其实就是一个js对象,每个dom节点就是这个对象里面的属性。因此,操作dom其实就是修改js对象的属性值,这个js对象改变以后,会触发浏览器的一些行为,这个行为(layout和paint)就是代价大、耗性能的源头了。 layout就是布局变动造成重新计算(耗cpu,有时也耗内存);paint就是调用浏览器UI引擎进行渲染展示页面(耗cpu和内存)
浏览器不会蠢到对于每次操作都马上回应,它是lazy的。
对于css和js都会把layout行为的数据缓存到一个队列中,当上下文完成执行后进行一次layout(css:结点的样式描述完 js:一个代码块执行完) 浏览器的lazy会带来一个问题,layout信息在队列中的时候,我所需要获取dom节点信息就获取不到了。这时浏览器就会提前执行一次layout,造成了dom操作耗性能耗资源的局面(代价比较大的原因) 特别提出,动画的每一帧都会导致layout,动画元素要绝对定位,脱离文档流。 dom操作太过频繁会怎么样呢?手机端会因为内存爆满或cpu占用过度而出现浏览器闪退现象
|