事件循环之js操作dom的执行顺序【代码执行】

115 阅读1分钟

代码

function demo(){
  const now = new Date().valueOf()
  document.body.style.backgroundColor = 'red';
  while ((new Date()).valueOf() - now <= 2000) {
   continue;
  }
  document.body.style.backgroundColor = 'blue';
}
demo(); //执行结果是什么样子的?

真正的答案是没有变红,直接两秒后变蓝

究其原因,看下以下代码就知道哦

function demo1(){
  setTimeout(function () {
      Promise.resolve().then(function () {
          const now = new Date().valueOf()
          document.body.style.backgroundColor = 'red';
          while ((new Date()).valueOf() - now <= 2000) {
           continue;
          }

          console.log('微任务完成')
      })
      const now = new Date().valueOf()
      while ((new Date()).valueOf() - now <= 2000) {
      }
      console.log('宏任务完成')
  }, 1000)
}
demo1(); //执行结果是什么样子的?

由上可知,js对dom的操作是在任务队列里的微任务都执行结束后才执行的

js代码执行由js引擎线程负责

dom样式更改由GUI渲染线程负责

所以两个线程互斥,造成了dom操作“异步”的效果

而且GUl渲染会进行优化,多个同-dom的操作会合并

执行一个宏任务(栈中没有就从事件队列中获取)

执行过程中如果遇到微任务,就将它添加到微任务的任务队列中

宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)

当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染>

渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

image.png

解决办法

  1. 将js线程手动滞后,通过setTimeout,js操作被放到下一个宏任务里。
function demo(){
  const now = new Date().valueOf()
  document.body.style.backgroundColor = 'red';
  setTimeout(()=> {
    while ((new Date()).valueOf() - now <= 2000) {
     continue;
    }
    document.body.style.backgroundColor = 'blue';
  })
}
demo(); //执行结果是什么样子的?
  1. 可以让GUI线程提前,通过触发回流的方法,让GUI线程提前执行。