事件循环 and "宏任务,微任务"面试题万能模板💁🏻‍♂️

289 阅读4分钟

一、事件循环 eventloop

  • 提到事件循环时,是指宿主环境的事件循环,而不是js中的事件循环。

  • js运行在宿主环境(浏览器、nodejs)中

  • 浏览器本身是一个复杂的系统,它要做的事情非常多,例如: 执行js代码,请求图片资源,解析css,渲染页面,响应鼠标的点击等等。在实现层面,浏览器内部会用不同的功能模块去完成不同的事情。这些不同的模块就体现为进程

  • 进一步把进程进行划分:

    1. 主进程。用来协调控制其他子进程。
    2. GPU进程。用于3D绘制等。
    3. 渲染进程。就是我们说的浏览器内核,负责具体页面的渲染,脚本执行,事件处理等。浏览器的每个tab页背后就有一个渲染进程。(线程可以进一步划分为多个线程
  • 一个渲染进程包括:

    1. 主线程。统一调度其他线程
    2. GUI渲染线程。负责渲染页面,布局和绘制。与JS引擎互斥。
    3. JS引擎线程。负责处理解析和执行javascript脚本程序。与GUI渲染线程互斥的
    4. 事件触发线程。用来控制事件循环(鼠标点击、setTimeout、ajax等)。当事件满足触发条件时,将事件放入到JS引擎所在的执行队列中。
    5. setInterval与setTimeout所在的线程。定时任务并不是由JS引擎计时的,是由定时触发线程来计时的。计时完毕后,通知事件触发线程
    6. 异步http请求线程。浏览器有一个单独的线程用于处理AJAX请求,当请求完成时,若有回调函数,通知事件触发线程。
    7. io线程。用来接收其他进程的消息。
  • 每个渲染进程(一个tab页)都有一个主线程,并且主线程非常繁忙,既要处理 DOM,又要计算样式,还要处理布局,同时还需要处理JavaScript 任务以及各种输入事件。要让这么多不同类型的任务在主线程中有条不紊地执行,这就需要一个系统来统筹调度这些任务,这个统筹调度系统就是消息队列和事件循环

    1. 主线程上要做很多事情,例如:js代码执行,页面布局计算,渲染等
    2. 主线程同一时刻只能做一件事,事情多了就要排队。所以主线程维护了任务队列。
    3. 某个事件发生时,事件触发线程 就把对应的任务添加到主线程的任务队列中。
    4. 主线程上的任务完成之后,就会从任务队列中取出任务来执行。

任务是以事件及其回调的方式存在的。当事件(用户的点击,图片的成功加载)发生时,将其回调添加到任务队列;主线程上的任务完成之后,就会从任务队列中取出任务来执行,此过程不断重复从而形成一个循环,称为事件循环eventLoop。

二、微任务和宏任务

  • 同步任务和异步任务

    • 同步任务:同步任务不需要进行等待,必须立即看到执行结果,比如console
    • 异步任务:异步任务需要等待一定的时候才能看到结果,比如setTimeout、网络请求
    • 异步任务,又可以细分为宏任务微任务
任务(代码)宏/微 任务环境
script宏任务浏览器
事件宏任务浏览器
网络请求(Ajax)宏任务浏览器
setTimeout() 定时器宏任务浏览器 / Node
fs.readFile() 读取文件宏任务Node
Promise.then()微任务浏览器 / Node
  • 万能模板

image.png

  • 示例:

console.log('1')
new Promise((resolve, reject) => {
  resolve('2')
}).then((res) => {
  console.log(res)
})
setTimeout(() => {
  console.log('3')
})
new Promise((resolve, reject) => {
  resolve('4')
}).then((res) => {
  console.log(res)
})
console.log('5')
  • 先执行微任务,再执行宏任务

image.png

image.png

  • test

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>
    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>
 // 1,2,4,6,7,10,3,9,8,5
 <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>
// 11,14,12,15,13
  • answer
    • 1、1,3,1000,2
    • 2、1,5,10,6,2,3,4,7,8,9
    • 3、1,3,4,5,1000,2
    • 4、3,2,1
    • 5、2,3,6,p2, p1, 1, 4, 5