宏任务和微任务、事件循环

一、宏任务和微任务、事件循环

JavaScript是单线程的,也就是说,同一个时刻,JavaScript只能执行一个任务,其他任务只能等待。

js是运行于浏览器的脚本语言,因其经常涉及操作dom,如果是多线程的,也就意味着,同一个时刻,能够执行多个任务。

  • 一个线程修改dom,另一个线程删除dom,那么浏览器就不知道该先执行哪个操作。所以js执行的时候会按照一个任务一个任务来执行。

二、同步任务和异步任务

js的任务都是同步的,那么遇到定时器、网络请求等这类型需要延时执行的任务会发生什么?

页面可能会瘫痪,需要暂停下来等待这些需要很长时间才能执行完毕的代码
所以,又引入了异步任务。

  • 同步任务:同步任务不需要进行等待可立即看到执行结果,比如console
  • 异步任务:异步任务需要等待一定的时候才能看到结果,比如setTimeout、网络请求

三、事件循环(Event Loop)

事件循环的比较简单,它是一个在 "JavaScript 引擎等待任务""执行任务""进入休眠状态等待更多任务" 这几个状态之间转换的无限循环。

引擎的一般算法:

  1. 当有任务时:
    ○ 从最先进入的任务开始执行。
  2. 没有其他任务,休眠直到出现任务,然后转到第 1 步。

四、任务队列

事件循环是通过任务队列的机制来进行协调的。

  • 一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合;

  • 每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。

  • setTimeout/Promise 等API便是任务源。

在事件循环中,每进行一次循环的关键步骤如下:

  • 在此次循环中选择最先进入队列的任务(oldest task),如果有则执行(一次)
  • 检查是否存在 微任务(Microtasks),如果存在则不停地执行,直至清空 微任务队列(Microtasks Queue)
  • 更新 render(DOM渲染)
  • 以上为一次循环,主线程重复执行上述步骤

在上述循环的基础上需要了解几点:

  • JS分为同步任务和异步任务
  • 同步任务都在主线程上执行,形成一个执行栈
  • 主线程之外,宿主环境管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
  • 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。

五、宏任务

(macro)task,可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。

任务(代码) 宏任务 环境
script 宏任务 浏览器
事件 宏任务 浏览器
网络请求(Ajax) 宏任务 浏览器
setTimeout() 定时器 宏任务 浏览器 / Node
fs.readFile() 读取文件 宏任务 Node

六、微任务

微任务(microtask)是宏任务中的一个部分,它的执行时机是在同步代码执行之后,下一个宏任务执行之前。
微任务包含:

Promise.then
await

比如一个人,去银行存钱,存钱之后,又进行了一些了操作,比如买纪念币、买理财产品、办信用卡,这些就叫做微任务。

七、运行机制

在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:

  • 执行一个宏任务(执行栈中没有就从事件队列中获取)
  • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
  • 宏任务里的同步代码执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
for ( let i = 1; i <= 宏任务.length; i++ ) {
    
    
  // 1.执行同步代码
  // 2.遇到其他宏任务,放到宏任务队列
  // 3.遇到其他微任务,放到微任务队列
  // 执行完同步代码,按照顺序执行微任务,直至清空微任务队列
  // 如果有DOM操作,执行DOM操作
  // i++ 下一次循环
}

八、面试题

1.

console.log(1)
setTimeout(function() {
    
    
  console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {
    
    
  resolve(1000)
})
p.then(data => {
    
    
  console.log(data)
})
console.log(3)

2.

console.log(1)
setTimeout(function() {
    
    
  console.log(2)
  new Promise(function(resolve) {
    
    
    console.log(3)
    resolve()
  }).then(function() {
    
    
    console.log(4)
  })
})
new Promise(function(resolve) {
    
    
  console.log(5)
  resolve()
}).then(function() {
    
    
  console.log(6)
})
setTimeout(function() {
    
    
  console.log(7)
  new Promise(function(resolve) {
    
    
    console.log(8)
    resolve()
  }).then(function() {
    
    
    console.log(9)
  })
})
console.log(10)

3.

console.log(1)
  setTimeout(function() {
    
    
    console.log(2)
  }, 0)
  const p = new Promise((resolve, reject) => {
    
    
    console.log(3)
    resolve(1000) // 标记为成功
    console.log(4)
  })
  p.then(data => {
    
    
    console.log(data)
  })
  console.log(5)

4.

new Promise((resolve, reject) => {
    
    
    resolve(1)
    new Promise((resolve, reject) => {
    
    
      resolve(2)
    }).then(data => {
    
    
      console.log(data)
    })
  }).then(data => {
    
    
    console.log(data)
  })
  console.log(3)

5.

setTimeout(() => {
    
    
  console.log(1)
}, 0)
new Promise((resolve, reject) => {
    
    
  console.log(2)
  resolve('p1')
  new Promise((resolve, reject) => {
    
    
    console.log(3)
    setTimeout(() => {
    
    
      resolve('setTimeout2')
      console.log(4)
    }, 0)
    resolve('p2')
  }).then(data => {
    
    
    console.log(data)
  })
  setTimeout(() => {
    
    
    resolve('setTimeout1')
    console.log(5)
  }, 0)
}).then(data => {
    
    
  console.log(data)
})
console.log(6)

6.

<!-- 如果有多个script,则优先执行script,再执行定时器 -->
<script>
    console.log(1);
    async function fnOne() {
    
    
      console.log(2);
      await fnTwo(); // 右结合先执行右侧的代码, 然后等待
      console.log(3);
    }
    async function fnTwo() {
    
    
      console.log(4);
    }
    fnOne();
    setTimeout(() => {
    
    
      console.log(5);
    }, 2000);
    let p = new Promise((resolve, reject) => {
    
     // new Promise()里的函数体会马上执行所有代码
      console.log(6);
      resolve();
      console.log(7);
    })
    setTimeout(() => {
    
    
      console.log(8)
    }, 0)
    p.then(() => {
    
    
      console.log(9);
    })
    console.log(10);
  </script>
 <script>
    console.log(11);
    setTimeout(() => {
    
    
      console.log(12);
      let p = new Promise((resolve) => {
    
    
        resolve(13);
      })
      p.then(res => {
    
    
        console.log(res);
      })
      console.log(15);
    }, 0)
    console.log(14);
  </script>

猜你喜欢

转载自blog.csdn.net/m0_62181310/article/details/126924489