事件环Event loop
概念
进程
问题:什么是进程?
一个cpu进程,为了任务正常运行分配调度出一个单位,来供其运行一个任务,进程是计算机调度的基本单位,进程包含多个线程,线程在进程中运行
在浏览器中的进程:
它是由多个进程组成的应用程序,分为主进程和辅助进程,每个tab页都会开启一个进程
- 主进程:用户界面
tab页:- 渲染进程(浏览器内核
Renderer渲染引擎) - 网络进程(网络请求)
GPU进程(动画与3D绘制)- 插件工具进程(
devtool)
- 渲染进程(浏览器内核
渲染进程:
它包括GUI渲染线程(渲染页面)和JS引擎线程(处理JS脚本程序),两者线程运行互斥
- 当
JS引擎线程工作的时候,GUI渲染线程(有队列)会空闲停止工作 - 当
JS引擎线程工作完成时,GUI渲染线程(有队列)会继续工作,渲染更新
线程
在渲染进程中的GUI渲染线程:
- 解析
HTML,CSS - 构建
DOM/render树 - 初始布局与绘制
- 重绘与回流
在渲染进程中的JS内核引擎线程:
- 一个主线程与多个辅助线程配合
- 一个浏览器只有一个
JS引擎 - 解析
JS脚本,运行JS代码
在渲染进程中的事件触发线程:
事件触发线程即事件环Event Loop线程
- 用户交互事件
setTimeoutAjax
在渲染进程中的宏任务和微任务:
创建线程的目的是为了实现异步的执行条件
问题:Mutation Observer是什么?
Mutation Observer(变动观察器)是监视DOM变动的接口。当DOM对象树发生任何变动时,Mutation Observer会得到通知。
宏任务:
- 宿主提供的异步方法和任务
script整体脚本代码setTimeout UI渲染 | #宏任务 | 浏览器 | Node | | —- | :—-: | —-: | | I/O | ✅ | ✅ | | setTimeout | ✅ | ✅ | | setInterval | ✅ | ✅ | | setImmediate | ❌ | ✅ | | requestAnimationFrame | ✅ | ❌ |
微任务:
- 语言标准(
ECMA262)提供的API运行 PromiseMutation Observer:接口提供了监视对DOM树所做更改的能力 | #微任务 | 浏览器 | Node | | —- | :—-: | —-: | | process.nextTick | ❌ | ✅ | | MutationObserver | ✅ | ❌ | | Promise.then catch finally | ✅ | ✅ |
问题:为什么要区分宏任务和微任务?
主要是实现优先级的问题,微任务实际上是优先级要高于宏任务,每一次微任务在执行队列里的任务执行完毕后要清空微任务当前任务队列,也就是先对微任务进行处理,然后去渲染,去执行宏任务
问题:为什么Promise.then要优先于下一个宏任务去处理?
因为它跟后续的代码也是保持异步关系,处理Promise,微任务去处理Promise的then回调
流程
事件环的运行流程分析:
JS引擎线程(执行栈):- 同步代码
- 宏任务异步代码
微任务队列(清空所有):
Promise.thenMutation Observer:接口提供了监视对DOM树所做更改的能力
GUI渲染宏任务队列(取出一个放入执行栈,先进先出):
Ajax(响应回来进执行栈)setTimeout(时间到了进执行栈)- 用户交互事件(事件被触发进执行栈)
JS引擎线程(接收异步的回调)…
代码分析:
//宏任务: script脚本document.body.style.backgroundColor = 'orange';console.log(1);//宏任务: setTimeout//微任务: setTimeout里的回调setTimeout(() => {document.body.style.backgroundColor = 'green';console.log(2);}, 100);//微任务: then里的回调Promise.resolve(3).then(num => {document.body.style.backgroundColor = 'pink';console.log(num);});//宏任务: script脚本console.log(4);
注意:
关于
Promise,它是根据后面是否跟有then来决定是否进入微任务队列的
- 如果没有跟
then,它是同步关系,不会进入微任务队列,不会挂起- 如果有跟
then,它是异步关系,会进入微任务队列,会挂起
//代码流程:JS引擎线程执行栈:1. script脚本 document.body.style.backgroundColor = 'orange'; 没渲染2. script脚本 console.log(1); //13. script脚本 console.log(4); //4⬇微任务队列:4. 执行Promise.resolve(3).then()里面的回调;5. document.body.style.backgroundColor = 'pink';6. console.log(num); //37. 清空微任务队列⬇GUI渲染; //backgroundColor = 'pink'; 渲染成功⬇宏任务队列:8. setTimeout(); 根据延迟时间到点决定是否放入队列9. setTimeout(cb); 将回调放入JS执行栈⬇JS引擎线程执行栈:10. 去执行回调 document.body.style.backgroundColor = 'green'; 没渲染11. 去执行回调 console.log(2); //2⬇微任务队列:已清空⬇GUI渲染; //backgroundColor = 'green'; 渲染成功⬇宏任务队列:空⬇JS引擎线程执行栈:空
问题:为什么宏任务和微任务都要滞后(异步)?
都是为了模拟一个多线程,跟同步代码是滞后关系
练习
题目1:
Promise.resolve().then(() => {console.log('p1');setTimeout(() => {console.log('s2');}, 0);});setTimeout(() => {console.log('s1');Promise.resolve().then(() => {console.log('p2');});}, 0);
//代码流程:JS引擎线程执行栈:1. 空⬇微任务队列:2. 执行Promise.resolve().then()里面的回调;3. console.log('p1'); //p14. 清空微任务队列⬇GUI渲染;⬇宏任务队列:5. setTimeout();6. setTimeout();⬇JS引擎线程执行栈:7. 去执行setTimeout回调 console.log('s1'); //s111. 去执行setTimeout回调 console.log('s2'); //s2⬇微任务队列:8. 执行Promise.resolve().then()里面的回调;9. console.log('p2'); //p210. 清空微任务队列⬇GUI渲染;⬇宏任务队列:空⬇JS引擎线程执行栈:空//打印结果:p1s1p2s2
题目2:
console.log(1);setTimeout(() => {console.log(2);}, 10);new Promise(function(resolve, reject){console.log(3);resolve('');console.log(4);}).then(res => {console.log(5);});console.log(6);
//分析://考点://1.new Promise(resolve函数) 里面的resolve是同步回调程序//2.new Promise(resolve函数)//如果resolve执行会有then 没有执行没有then//如果reject执行会有catch 没有执行没有catchJS执行栈:1.console.log(1); //1 -> x2.new Promise()3.new Promise 执行resolve回调 //3 //4 -> x4.console.log(6); //6 -> x9.setTimeout1 cb 等待10 进入JS执行栈 然后执行 //2 -> x微任务:7.new Promise().then微任务队列:8.new Promise().then 执行回调 //5 -> x宏任务:5.setTimeout1宏任务队列:6.setTimeout1 cb打印结果:134652
题目3:
console.log(1);setTimeout(() => {Promise.resolve().then(() => {console.log(2);});}, 10);new Promise(function(resolve, reject){console.log(3);resolve('');console.log(4);}).then(res => {setTimeout(() => {console.log(5);}, 0);});console.log(6);
//分析:JS执行栈:1.console.log(1); //1 -> x4.new Promise()5.new Promise 执行resolve回调 //3 //4 -> x6.console.log(6); //6 -> x12.setTimeout2 cb 等待时间短 先进执行栈 执行 //5 -> x13.setTimeout2 cb 等待时间长 后进执行栈 执行微任务:7.new Promise().then14.Promise.resolve().then微任务队列:8.new Promise().then 执行回调11.清空微任务队列15.Promise.resolve().then 执行回调 //2 -> x16.清空微任务队列宏任务:2.setTimeout19.setTimeout2宏任务队列:3.setTimeout1 cb10.setTimeout2 cb打印结果:134652
题目4:
let res = function(){console.log(1);return new Promise((resolve, reject) =>{console.log(2);resolve(4);});}new Promise(async(resolve, reject) => {console.log(3);let test = await res();console.log(test);});console.log(5);new Promise((resolve, reject) => {console.log(6);});console.log(7);
//分析://考点:async/await语法糖//async函数默认返回一个promise实例//await 必须存在在async函数中//await test() 跟 yield是一回事//test()返回一个promise实例对象,那么await test()就会调用test().then()JS执行栈:1.fn赋值给res2.new Promise1(); 执行回调 //33.遇到await 进微任务队列9.console.log(5); //510.new Promise2(); 执行回调 //611.console.log(7); //7微任务:4.res().then()7.test().then()微任务队列:5.res().then() 执行res回调 //1 //26.let test = await res(); -> test().then() -> 进入微任务8.清空微任务队列12.执行test().then() //console.log(test); //413.清空微任务队列打印结果:3125674
题目5:
let res = function(){console.log(1);return new Promise((resolve, reject) =>{setTimeout(() => {new Promise((resolve) => {console.log(2);setTimeout(() => {console.log(3);});});}, 0);resolve(5);});}new Promise(async(resolve, reject) => {setTimeout(() => {Promise.resolve().then(() => console.log(4));}, 0);let test = await res();console.log(test);});setTimeout(() => {console.log(6);});new Promise((resolve, reject) => {setTimeout(() => {console.log(7);}, 0);});console.log(8);
//分析:JS执行栈:1.fn赋值给res2.new Promise1(); 执行回调 是宏任务且进队列5.遇到await 进微任务队列13.let test = await res(); -> test().then() -> 进入下一轮微任务17.new Promise3(); 执行回调 是宏任务且进队列20.console.log(8); //8微任务:6.res().then() -> x14.test().then() -> x24.Promise.resolve().then() -> x微任务队列:7.res().then() 执行res() //18.new Promise2(); 执行回调 是宏任务且进队列11.resolve(5); 抛出512.清空微任务队列21.test().then()执行回调 //522.清空微任务队列24.Promise.resolve().then()执行 //425.清空微任务队列宏任务:3.setTimeout19.setTimeout215.setTimeout318.setTimeout427.setTimeout5宏任务队列:4.setTimeout1 cb10.setTimeout2 cb16.setTimeout3 cb19.setTimeout4 cb23.setTimeout1 cb 执行回调 遇到Promise.resolve().then()微任务26.setTimeout2 cb 执行回调 遇到new Promise() 执行 //2 再遇到宏任务28.setTimeout5 cb29.setTimeout3 cb 执行 //630.setTimeout4 cb 执行 //731.setTimeout5 cb 执行 //3打印结果:18542673
