浏览器原理
学习浏览器原理对于我们开发是很有必要的 我们可以了解到浏览器内部工作原理对自己的代码进行优化
进程线程
首先了解进程和线程
进程就就是内存在正在进行的应用程序 在内存中独占一个内存空间 并且进程之间是隔离的 可以看到每个应用都有一个进程 占用空间内存 那这块空间内存就可以理解为进程
线程
线程是进程最小的执行单位 一个进程至少有一个线程 进程开启后会创建一个线程运行代码 如果程序同时执行多块代码 主线程就会启动更多线程执行代码
一个进程可以有多个线程 进程大于线程
打开浏览器之后最少开启了四个进程
1 浏览器主进程 主要显示 页面展示 用户交互 子进程管理 提供储存等等
2 渲染进程 核心任务是将html css javascript变成用户可以交互的网页
Chrome为每个tab页创建了一个渲染进程
3 网络 负责网络资源请求
4 插件进程 负责插件运行 因为插件容易崩溃
事件循环
知道了线程进程的概念 下面我们可以看一下事件循环
1 会开启一个渲染进程
执行html css js 代码 保证页签不受影响
2 浏览器内部任务是排队的
1 最开始的时候 渲染主线程的时候会开启一个无限循环
2 每次循环的时候会检查消息队列中是否有任务 有的话就取出第一个任务 取出第一个任务执行 执行完一个后进入下一次循环 如果没有则进入休眠状态
3 其他所有线程(包括其他进程的线程)可以随时向消息队列中添加任务 任务会加到消息队列的末尾 添加新任务的时候 如果主线程是休眠状态 则会继续唤醒循环拿取任务
异步
什么是异步任务 ? 代码执行无法立即处理的任务
如果定时器是同步的
则会阻塞线程 所以解决方案是 当计时结束的时候将回调函数放到消息队列的队尾
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>哈哈哈</h1>
<button>点击我</button>
<script>
var h1=document.querySelector('h1')
var btn=document.querySelector('button')
function delay(duration){
var star=Date.now();
while(Date.now()-star<duration){}
}
btn.onclick=function(){
h1.textContent="666"
//3m后重新绘制
delay(3000)
}
</script>
</body>
</html>
任务的优先级
刚刚说了任务是在线程中排队的 其中异步任务是结束的时候回调将任务放到队尾排队
任务是没有优先级的 但消息队列中优先级 同一个类型任务必须在一个任务队列 在一次事件循环中可以取出不同的队列执行任务
览器必须有个微队列 优先于其他所有任务执行
例子
同时等待一秒钟 然后执行2在执行1
例子
先执行全局的3 然后执行微队列中的2 在执行延时队列中的1
<script>
setTimeout(()=>{
console.log(1);
},0)
Promise.resolve().then((function(){
console.log(2);
}))
console.log(3);
//3 2 1
</script>
例子
先执行3 在执行2 然后继续执行微队列中的12888 在执行1
<script>
setTimeout(()=>{
console.log(1);
},0)
Promise.resolve()
.then((function(){
console.log(2);
}))
.then(()=>{
console.log(12888);
})
console.log(3);
//3 2 1288 1
</script>
例子
执行全局的5 在执行微任务4 在执行3 将a推入微任务队列 执行1 继续将function推入微任务队列 2
<script>
function a(){
console.log(1);
Promise.resolve().then(function(){
console.log(2);
})
}
setTimeout(()=>{
console.log(3);
Promise.resolve().then((a))
},0)
Promise.resolve().then(()=>{
console.log(4);
})
console.log(5);
//5 4 3 1 2
</script>
问答
面试题:如何理解 JS 的异步?
参考答案:
JS是一门单线程的语言,这是因为它运行在浏览器的渲染主线程中,而渲染主线程只有一个。
而渲染主线程承担着诸多的工作,渲染页面、执行 JS 都在其中运行。
如果使用同步的方式,就极有可能导致主线程产生阻塞,从而导致消息队列中的很多其他任务无法得到执行。
这样一来,一方面会导致繁忙的主线程白白的消耗时间,另一方面导致页面无法及时更新给用户造成本死现象。
所以浏览器采用异步的方式来避免。具体做法是当某些任务发生时,比如计时器、网络、事件监听,主线程将任务交给其他线程去处理,自身立即结束任务的执行,转而执行后续代码。当其他线程完成时,将事先传递的回调函数包装成任务,加入到消息队列的末尾排队,等待主线程调度执行。
在这种异步模式下,浏览器永不阻塞,从而最大限度的保证了单线程的流畅运行。
阐述一下 JS 的事件循环
参考答案:
事件循环又叫做消息循环,是浏览器渲染主线程的工作方式。
在 Chrome 的源码中,它开启一个不会结束的 for 循环,每次循环从消息队列中取出第一个任务执行,而其他线程只需要在合适的时候将任务加入到队列末尾即可。
过去把消息队列简单分为宏队列和微队列,这种说法目前已无法满足复杂的浏览器环境,取而代之的是一种更加灵活多变的处理方式。
根据 W3C 官方的解释,每个任务有不同的类型,同类型的任务必须在同一个队列,不同的任务可以属于不同的队列。不同任务队列有不同的优先级,在一次事件循环中,由浏览器自行决定取哪一个队列的任务。但浏览器必须有一个微队列,微队列的任务一定具有最高的优先级,必须优先调度执行。
总结
之前是说宏任务微任务 但最新版改成了微任务队列 更加好理解了
下周还是把剩余的知识点学完准备收拾收拾写项目了