宏观任务和微观任务(Macro Task and Micro Task)

904 阅读2分钟

视图

image

介绍

这是我参与更文挑战的第1天,活动详情查看:更文挑战

js中,有两类任务队列:宏任务队列(macro tasks)和微任务队列(micro tasks)。宏任务队列可以有多个,微任务队列只有一个。那么什么任务,会分到哪个队列呢?

  • 宏任务:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering.
  • 微任务:process.nextTick, Promise, Object.observer, MutationObserver.
  • 微任务始终先于宏任务执行。
setTimeout(()=>console.log("d"), 0)
var r = new Promise(function(resolve, reject){
    resolve()
});
r.then(() => {
    var begin = Date.now();
    while(Date.now() - begin < 1000);
    console.log("c1")
    new Promise(function(resolve, reject){
        resolve()
    }).then(() => console.log("c2"))
});

// output: c1
// output: c2
// output: d

这里我们强制了1秒的执行耗时,这样,我们可以确保任务c2是在d之后添加到任务队列。但还是c2先于d输出了。

var r = new Promise(function(resolve, reject){
    console.log("a");
    resolve()
});
r.then(() => console.log("c"));
console.log("b")

// output: a
// output: b
// output: c

分析异步执行的顺序

  • 首先分析有多少个宏任务
  • 在每个宏任务中,分析有多少个微任务
  • 根据调用次序,确定宏任务中的微任务和执行次序
  • 根据宏任务的触发规则和调用次序,确定宏任务的执行次序
  • 确定整个顺序
console.log('script start');

// 微任务
Promise.resolve().then(() => {
    console.log('p1');
});

// 宏任务
setTimeout(() => {
    console.log('setTimeout');
}, 0);

var s = new Date();
while(new Date() - s < 50); // 阻塞50ms

// 微任务
Promise.resolve().then(() => {
    console.log('p2');
});

console.log('script ent');


/*** output ***/

// one macro task
script start
script ent

// all micro tasks
p1
p2

// one macro task again
setTimeout

上面之所以加50ms的阻塞,是因为 setTimeoutdelayTime 最少是 4ms. 为了避免认为 setTimeout 是因为4ms的延迟而后面才被执行的,我们加了50ms阻塞。

小思考

例题:(来自winter的重学前端) - 我们现在要实现一个红绿灯,把一个圆形 div 按照绿色 3 秒,黄色 1 秒,红色 2 秒循环改变背景色,你会怎样编写这个代码呢?

思路: - 用promise把setTimeout函数封装成为可用于异步的函数

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    .trafic-light {
      height: 100px;
      width: 100px;
      border-radius: 50%;
      margin: 200px auto 0;
      border: 1px solid #ececec;
    }
  </style>
</head>
<body>
  <div id="trafic-light" class="trafic-light"></div>
</body>
<script>
  let traficEle = document.getElementById('trafic-light');
  function changeTraficLight(color, duration) {
    return new Promise(function (resolve, reject){
      traficEle.style.background = color;
      setTimeout(resolve, duration);
    })
  }
  async function traficScheduler() {
    await changeTraficLight('green', 3000)
    await changeTraficLight('yellow', 1000)
    await changeTraficLight('red', 2000)
    traficScheduler();
  }
  traficScheduler();
</script>
</html>