宏任务和微任务、事件循环、面试题

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

二.为什么JavaScript是单线程的

js是运行于浏览器的脚本语言,因其经常涉及操作dom,所以设置为单线程操作。如果是多线程的,也就意味着,同一个时刻,能够执行多个任务。试想,如果一个线程修改dom,另一个线程删除dom,那么浏览器就不知道该先执行哪个操作。所以js执行的时候会按照一个任务一个任务来执行。

三. 为什么任务要分为同步任务和异步任务

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

页面可能会瘫痪,需要暂停下来等待这些需要很长时间才能执行完毕的代码

所以,又引入了异步任务。

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

四.事件循环

浏览器本身是⼀个复杂的系统,它要做的事情⾮常多,例如: 执⾏js代码,请求图⽚资源,解 析css,渲染⻚⾯,响应⿏标的点击等等。在实现层⾯,浏览器内部会⽤不同的功能模块去完成不同的 事情。这些不同的模块就体现为进程。

image.png

image.png

image.png

进⼀步把进程进⾏划分:

  1. 主进程。⽤来协调控制其他⼦进程。

  2. GPU进程。⽤于3D绘制等。

  3. 渲染进程。就是我们说的浏览器内核,负责具体⻚⾯的渲染,脚本执⾏,事件处理等。每个tab⻚背 后就有⼀个渲染进程。

进程这个单位还是⽐较⼤,它进⼀步拆分多个线程。可以理解为⼀个⻚⾯上的事还是⽐较多,要多找些 ⼩弟来完成。具体来说,⼀个渲染进程包括:

  1. 主线程。统⼀调度

  2. GUI渲染线程。负责渲染⻚⾯,布局和绘制。与JS引擎互斥。

  3. JS引擎线程。负责处理解析和执⾏javascript脚本程序。

  4. 事件触发线程。⽤来控制事件循环(⿏标点击、setTimeout、ajax等)。当事件满⾜触发条件时, 将事件放⼊到JS引擎所在的执⾏队列中。

  5. setInterval与setTimeout所在的线程。定时任务并不是由JS引擎计时的,是由定时触发线程来计时 的。计时完毕后,通知事件触发线程

  6. 异步http请求线程。浏览器有⼀个单独的线程⽤于处理AJAX请求,当请求完成时,若有回调函数, 通知事件触发线程。

  7. io线程。⽤来接收其他进程的消息。

每个渲染进程都有⼀个主线程,并且主线程⾮常繁忙,既要处理 DOM,⼜要计算样式,还要处理布 局,同时还需要处理 JavaScript 任务以及各种输⼊事件。要让这么多不同类型的任务在主线程中有条不 紊地执⾏,这就需要⼀个系统来统筹调度这些任务,这个统筹调度系统就是消息队列和事件循环。

任务有很多,⼈只有⼀个,且任意时刻只能做⼀件事(不是⼀边⾛路⼀边听课这种事哈),那怎么办, 就是排队呗

image.png

这张图非常生动形象,大家可以收藏

  1. 主线程上要做很多事情,例如:js代码执⾏,⻚⾯布局计算,渲染等

  2. 主线程同⼀时刻只能做⼀件事,事情多了就要排队。所以主线程维护了任务队列。

  3. 某个事件发⽣时,事件触发线程 就把对应的任务添加到主线程的任务队列中。

  4. 主线程上的任务完成之后,就会从任务队列中取出任务来执⾏。

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

要点回顾:

● 事件循环不是js的语⾔层⾯的内容,是js的宿主环境(浏览器,nodeJS)的讨论内容。在js代码中 讨论事件循环是没有意义的。

● 在更⼴的领域。事件循环是⼀个典型的⽣产者/消费者模型。异步I/O,⽹络请求是事件的⽣产者,源 源不断提供事件,这些事件被传递到对应的观察者那⾥,事件循环则从观察者那⾥取出事件并处 理。

● 在windows下,这个循环基于IOCP创建,⽽在linux下基于多线程创建。

五.微任务和宏任务

1.宏任务和微任务

异步任务,⼜可以细分为宏任务和微任务。下⾯列举⽬前学过的宏任务和微任务。

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

六.⾯试题分析

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)
复制代码

效果:

image.png

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)
复制代码

效果:

image.png

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)
复制代码

效果:

image.png

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)
复制代码

效果:

image.png

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)
复制代码

效果:

image.png

七.总结

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

猜你喜欢

转载自juejin.im/post/7110483425492107294