事件循环
浏览器本身是一个复杂的系统,它要做的事情非常多,例如: 执行js代码,请求图片资源,解析css,渲染页面,响应鼠标的点击等等。在实现层面,浏览器内部会用不同的功能模块去完成不同的事情。这些不同的模块就体现为进程。
进一步把进程进行划分:
1.主进程。用来协调控制其他子进程。
2.GPU进程。用于3D绘制等。
3.渲染进程。就是我们说的浏览器内核,负责具体页面的渲染,脚本执行,事件处理等。每个tab页背后就有一个渲染进程。进程这个单位还是比较大,它进一步拆分多个线程。可以理解为一个页面上的事还是比较多,要多找些小弟来完成。具体来说,一个渲染进程包括:
4.主线程。统一调度
5. GUI渲染线程。负责渲染页面,布局和绘制。与JS引擎互斥。
6.JS引擎线程。负责处理解析和执行javascript脚本程序。
7.事件触发线程。用来控制事件循环(鼠标点击、setTimeout、ajax等)。当事件满足触发条件时,将事件放入到JS引擎所在的执行队列中。
8. setInterval与setTimeout所在的线程。定时任务并不是由JS引擎计时的,是由定时触发线程来计时的。计时完毕后,通知事件触发线程
9.异步http请求线程。浏览器有一个单独的线程用于处理AJAX请求,当请求完成时,若有回调函数,通知事件触发线程。
10. io线程。用来接收其他进程的消息。
每个渲染进程都有一个主线程,并且主线程非常繁忙,既要处理 DOM,又要计算样式,还要处理布局,同时还需要处理 JavaScript 任务以及各种输入事件。要让这么多不同类型的任务在主线程中有条不紊地执行,这就需要一个系统来统筹调度这些任务,这个统筹调度系统就是消息队列和事件循环。
任务有很多,人只有一个,且任意时刻只能做一件事(不是一边走路一边听课这种事哈),那怎么办,就是排队呗
eventLoop
主线程上要做很多事情,例如:js代码执行,页面布局计算,渲染等
主线程同一时刻只能做一件事,事情多了就要排队。所以主线程维护了任务队列。
某个事件发生时,事件触发线程 就把对应的任务添加到主线程的任务队列中。
主线程上的任务完成之后,就会从任务队列中取出任务来执行。
任务是以事件及其回调的方式存在的。当事件(用户的点击,图片的成功加载)发生时,将其回调添加到任务队列;主线程上的任务完成之后,就会从任务队列中取出任务来执行,此过程不断重复从而形成一个循环,称为eventLoop。
要点回顾:
● 事件循环不是js的语言层面的内容,是js的宿主环境(浏览器,nodeJS)的讨论内容。在js代码中讨论事件循环是没有意义的。
● 在更广的领域。事件循环是一个典型的生产者/消费者模型。异步I/O,网络请求是事件的生产者,源源不断提供事件,这些事件被传递到对应的观察者那里,事件循环则从观察者那里取出事件并处理。
● 在windows下,这个循环基于IOCP创建,而在*nix下基于多线程创建。
微任务和宏任务
为什么任务要分为同步任务和异步任务
试想一下,如果js的任务都是同步的,那么遇到定时器、网络请求等这类型需要延时执行的任务会发生什么?
页面可能会瘫痪,需要暂停下来等待这些需要很长时间才能执行完毕的代码
所以,又引入了异步任务。
● 同步任务:同步任务不需要进行等待可立即看到执行结果,比如console
● 异步任务:异步任务需要等待一定的时候才能看到结果,比如setTimeout、网络请求
宏任务和微任务
异步任务,又可以细分为宏任务和微任务。下面列举目前学过的宏任务和微任务。
![](https://i-blog.csdnimg.cn/blog_migrate/7a57168e7f60003be6926ecd69b313e3.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')
● 先执行同步代码
● 遇到宏任务,放入队列
● 遇到微任务,放入微任务队列
● 执行栈为空
○ 将微任务入栈执行
● 所有的微任务完成之后,取出宏任务队列来执行
同步代码 => 微任务 => 宏任务
示例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)
结果 : 1,3,1000,2
示例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)
结果 : 1,5,10,6,2,3,4,7,8,9
示例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)
结果 : 1,3,4,5,1000,2
示例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)
结果 : 3,2,1
示例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)
结果 : 2,3,6,p1,p2,1,4,5
示例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>
<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>
结果:
1,2,4,6,7,10,11,14,3,9,8,12,15,13,5
示例7:
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2(){
console.log('async2')
}
console.log('script start');
setTimeOut(function(){
console.log('setTimeout');},0);
async1();
new Promise(function(resolve){
console.log('promise1');
resolve();
}).then(function(){
console.log('promise2');
});
console.log('script end');
结果:
script start->async1 start->async2->promise1->script end->async1 end->promise2->setTimeout