Skip to content
On this page

任务队列

TIP

进程是计算机以进程来分配任务和调度任务
浏览器是由多个 进程组成的 (每个页签算是一个独立的进程)
浏览器的主要进程有 主进程,网络进程,绘图进程,插件进程

因为Js 是单线程,在主线程中只能做一件事,但是 像 ajax/定时器 是不需要 user-agent 参与,所以需要异步处理

异步的意思是他们不能依次执行,需要等待合适的时机去执行

由于是单线程,这个时机和同步代码的执行时机发生了冲突 所以需要引入事件循环机制(Event Loop)

执行顺序

在主线程执行宏任务时,遇到了微任务,会把微任务放入微任务队列中,等到当前的宏任务执行完毕之后,把所有的微任务统一执行

在执行微任务时,遇到了宏任务,放入宏任务队列,等到这一批微任务执行完毕,宏任务队列单独拿出一个,放入主线程执行
微任务执行一批,宏任务执行一个

微任务执行过后执行 GUI 渲染

在下面的例子中, 微任务 `Promise.resolve` 比 `setTimeout` 先执行, 此时的 `p` 的 `length` 是 2,**但是没有渲染,页面还是空白**

宏任务 setTimeout 执行时此时页面已经渲染了, 在页面可以看到 hello world

html
<body>
  <p> event Loop </p>
</body>
<script>
const p = document.createElement('p');
p.innerHTML = 'Hello World';
document.body.appendChild(p);

const list = document.getElementsByTagName('p');

console.log("length",list.length) // 2

setTimeout(()=>{
  const list = document.getElementsByTagName('p');
  console.log("setTimeout - length",list.length) // 2
  alert("setimeout阻塞")
})

// dom 渲染前,
Promise.resolve().then(()=>{
  const list = document.getElementsByTagName('p');
  console.log("setTimeout - length",list.length) // 2
  alert("Promise阻塞")
})
</script>

为什么要有微任务

微任务的出现,使得在宏任务执行完,到浏览器渲染之前,可以在这个阶段插入任务的能力。

INFO

一个 微任务(microtask)就是一个简短的函数,当创建该函数的函数执行之后,并且 只有当 Javascript 调用栈为空,而控制权尚未返还给被 user agent 用来驱动脚本执行环境的事件循环之前,该微任务才会被执行

这使得给定的函数在没有其他脚本执行干扰的情况下运行,也保证了微任务能在用户代理有机会对该微任务带来的行为做出反应之前运行

html
<html>
<head>
  <style>
    div {
      padding: 0;
      margin: 0;
      display: inline-block;
      widtH: 100px;
      height: 100px;
      background: blue;
    }
    #microTaskDom {
      margin-left: 10px;
    }
  </style>
</head>
<body>
  <div id="taskDom"></div>
  <div id="microTaskDom"></div>
  <script>
    window.onload = () => {
        setTimeout(() => {
          taskDom.style.background = 'red'
          setTimeout(() => {
            taskDom.style.background = 'black'
          }, 0);
          
          microTaskDom.style.background = 'red'
          Promise.resolve().then(() => {
            microTaskDom.style.background = 'black'
          })
        }, 1000);
    }
  </script>
</body>
</html>

如果使用 setTimeout 立马修改背景色,会闪现一次红色背景,而使用 Promise 则不会。
因为微任务会在渲染之前完成对背景色的修改,等到渲染时就只需要渲染黑色。
而使用宏任务,第一次渲染会渲染红色,等到下一次任务时修改为黑色,之后的渲染阶段才会再次渲染为黑色

宏任务以及微任务的出现,都是从用户体验以及性能方面进行考虑的,它们的出现可以让用户得到更好的使用体验。

假如脱离浏览器,有没有其它场景?

宏任务和微任务的出现,实际上是让 JS 脚本有了在渲染阶段前后可以完成一些操作的能力,类似于生命周期的概念。

所以像Vue、React的生命周期,Node.js 的事件循环都是一种场景。

面试题

js
console.log(1);
async function async () {
    console.log(2);
    await console.log(3);
    console.log(4)
}

setTimeout(() => {
	console.log(5);
}, 0);

const promise = new Promise((resolve, reject) => {
    console.log(6);
    resolve(7)
})

promise.then(res => {
	console.log(res)
})

async (); 
console.log(8);

// 1 6 2 3 8 
// 7 4 5