文章目录
一.浏览器多进程与js单线程:
https://segmentfault.com/a/1190000012925872
1.进程与线程:
进程是CPU资源(内存)分配的最小单位。
线程是CPU最小的调度单位。是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程。
2.浏览器的多进程:
1.浏览器主进程(Browser进程):(负责协调、主控)
- 负责浏览器界面显示,与用户交互。如前进,后退等
- 负责各个页面的管理,创建和销毁其他进程
- 将渲染进程得到的内存中的Bitmap,绘制到用户界面上
- 网络资源的管理,下载等
2.浏览器渲染进程(也称Render进程,浏览器内核,比如bink):内部是多线程的;每个Tab页面都有一个渲染进程。
- 页面渲染
- js脚本执行
- 事件处理
3.GPU进程:用于3D绘制等
4.第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建。
3.浏览器渲染进程中的线程:
牢记:内核即浏览器渲染进程;js引擎即js引擎线程。
(1)GUI渲染线程,也称UI线程
- 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
- 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
- GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起。
(2)js引擎线程
-
浏览器渲染进程中的线程大哥
-
也称为js引擎,js内核,比如V8引擎
-
js引擎线程负责处理Javascript脚本程序,执行代码。
-
JS引擎一直等待着任务队列(⭐️) 中任务的到来,然后加以处理。
-
每个tab页面都要一个渲染进程,每个渲染进程只有一个js引擎线程,也就是经常说的js引擎是单线程的。**为什么js引擎线程要设计成单线程呢?**因为js设计的主要目的是与用户进行交互,进行简单的dom操作,如果设计为多线程,则还需要处理同步等问题,就变得复杂了。
-
GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
(3)事件触发线程
- 用于控制事件,事件触发时(click,load)会将事件的处理函数推进事件队列
(4)定时器触发线程
- 浏览器定时计数器并不是由JavaScript引擎计数的。
- 浏览器通过单独线程来计时并触发定时,计时完毕后,添加到事件队列中,等待JS引擎空闲后执行。
(5)http异步请求线程
- 在通过XMLHttpRequest创建连接后,浏览器会新开一个线程来发送http请求。
- 将检测到状态变更时,如果设置有回调函数,http异步请求线程就会产生状态变更事件,将这个回调放入事件队列中,交由js引擎处理。
由于GUI渲染线程与js线程是互斥的,所以js可能会阻塞页面的加载。
4.WebWorker线程:
webwork是HTML5新增的用于在后台线程运行js脚本的方法。
- 创建Worker时,JS引擎向浏览器申请在当前渲染进程中开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM)
- JS引擎线程与worker线程间通过特定的方式通信(postMessage API,需要通过序列化对象来与线程交互特定的数据)
因此,耗时的操作可以开一个worker线程,这样就不会影响js线程的执行。
因此,并不能说webwork使得js引擎变成了多线程,js引擎还是单线程,只是现在为它开了一个新的线程来处理Js代码。
5.异步任务的由来:
js是单线程,一次只能完成一件任务,如果有多个任务,就必须排队。如果一个任务耗时很长,会拖延整个程序的执行,影响用户体验。为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。
6.js执行机制:
https://www.cnblogs.com/wangziye/p/9566454.html
js的执行机制就是 浏览器内核多个线程之间的配合 和 事件循环机制 :
1.同步任务会直接进行主线程的执行栈中,然后js先执行 执行栈中的同步任务
2.在执行的过程中,若遇到异步任务,则提交给对应的异步线程处理
3.在这些异步线程中,若监听到某异步任务已经被触发了,即发送了点击,或者定时器时间已到,则将其放入任务队列(task queue)中
4.执行栈中的同步任务执行完后,主线程会不断的轮询查找任务队列中的任务,并执行任务。
7.从任务列表中取出并执行异步任务的原则:
(1)异步任务的分类:
宏任务(macrotask):
script (整体代码),dom事件,ajax,setTimeout,setInterval,setImmediate,requestAnimationFrame,I/O,UI rendering
注意:setTimeout的优先级比setImmediate高
微任务(microtask):
promise.then,mutation回调,process.nextTick
注意:process.nextTick的优先级比promise.then高
(2)具体原则:
主线程中的执行栈在执行完同步任务之后,主线程会对事件队列(event queque)进行轮询,
会先去执行微任务队列中的所有任务,
执行完毕后再去执行宏任务队列中的一个任务,
每个宏任务执行结束后,在另一个宏任务执行前,需要检查微任务队列是否还存在任务,若存在,则需执行完才能执行下一个宏任务。
8.js执行机制分析题:
(1)第一题:超级大综合
//主线程直接执行
console.log('1');
//丢到宏事件队列中
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
//微事件1
process.nextTick(function() {
console.log('6');
})
//主线程直接执行
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
//微事件2
console.log('8')
})
//丢到宏事件队列中
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
//输出:1,7,6,8,2,4,3,5,9,11,10,12
(2)第二题
setTimeout(function() {
console.log(1)
}, 0);
new Promise(function(resolve, reject) {
console.log(2)
for (var i = 0; i < 10000; i++) {
if(i === 10) {console.log(10)}
i == 9999 && resolve();
}
console.log(3)
}).then(function() {
console.log(4)
})
console.log(5);
//最终执行结果:2 10 3 5 4 1
(3)第三题:考察setImmediate,process.next的优先级
setImmediate(function () {
console.log(1);
}, 0);
setTimeout(function () {
console.log(2);
}, 0);
new Promise(function (resolve) {
console.log(3);
resolve();
console.log(4);
}).then(function () {
console.log(5);
});
console.log(6);
process.nextTick(function () {
console.log(7);
});
console.log(8);
//输出结果是3 4 6 8 7 5 2 1
(4)第四题:考察process.next的优先级
setTimeout(function () {
console.log(1);
}, 0);
new Promise(function (resolve, reject) {
console.log(2);
resolve();
})
.then(function () {
console.log(3);
})
.then(function () {
console.log(4);
});
process.nextTick(function () {
console.log(5);
});
console.log(6);
// 输出为 2 6 5 3 4 1
(5)第五题:
//给出以下代码的执行结果,并说明原因
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
console.log(i);
//输出:先输出一个5,然后输出5个5
//原因:setTimeout属于异步任务,主线程中的同步代码执行完毕后才会去执行任务队列中的任务。