前言
以前有写过关于浏览器中关于js线程的文档,请移步 这里 查看!但以为偏过于技术化了,对于实际理解意义不大,因此想乘此机会用一种你们都能懂的话语方式来记录一下本身对 浏览器中js线程 的理解,以及创建在此基础一些优化方案!javascript
这篇文档不是技术文档,只是力求把相关概念用最土的话说清楚!css
由于这部分技术在我看来是前端理解性能的高级话题了!因此我不敢保证我写的会很好,亦不敢保证能必定带给你实质性的超越。我惟恐本身经验、理解有限,不免会有表达出错的地方!因此欢迎大家 issue ,咱们一切来参与起来把这个话题向更好的方向完善!html
什么是线程?
对于这个问题,我想没有必定的工做阅历沉淀或者相关专业方面的学习,恐怕难以叙述清除这个术语了!你要把这个问题问我,估计我也很难回答上来!但又如何?对于搞开发这种须要实际干事的工做来讲,恐怕只是让你正确把一个专业术语表达出来,恐怕对你的工做没有多大意义!咱们只要知道这个东西是干吗的就好了!至于怎么把这个术语表达清楚,那是专门干这行的人的工做!好了,到楼上首先仍是容许我把从搜索引擎上搜到的线程的描述粘贴在下面:
线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。前端
从上面的描述中大概知道了几个关键点:计算机给线程单独分派的空间、独立的单元、到楼上www.rfwcom.com可执行的执行单元及顺序控制流程!按个人理解就是“一个独立的程序顺序执行空间”!java
若是作个比喻的话:线程就比如一我的去开公司,注册公司、运营、销售、推广都是他一我的在干,并且是按照必定的顺序干,好比须要先要有项目、再找供应商、等项目成熟了,再注册公司,最后一步一步作大!这个顺序都是最开始企划好的!这些话换到浏览器里就是:咱们写的脚本只有一个执行单元来管理代码,而且按咱们写代码的顺序依次执行代码。看下面代码例子:浏览器
var a = 'a';
var b = 'b';
console.log(a);
console.log(b);
//由于打印a的操做在前面,而打印b的操做在后面,那么打印a的代码先执行,而打印b的操做后执行
顺便说一下多线程呗,顾名思义就是多个单线程一块儿来管理咱们的代码,好比上面那个开公司的例子,老板等公司作大了以后,他终于明白了:他一我的再牛、再能干,但是公司大到必定程度,他一我的是忙不过来的,即使他一我的能省一笔很大的请工人所带来的薪水开销!因而他肯定聘请工人来帮他工做,因而你们一块儿努力,共同把公司作得更大,这里每一个工人就至关于一个线程!多线程
多线程和单线程的区别:仍是拿前面开公司的例子来讲明,一我的(单线程)作的时候,本身想怎么作就怎么作,不用怕影响别人,并且消耗的社会资源(吃喝拉撒)也少,由于一我的再消耗也消耗不到哪里去!并且还能省下不少费用(工人费用等),可是问题公司怎么作都作不大,作来作去仍是一个小做坊,不能充分利用工人闲散资源来扩展公司等!多我的(多线程)一块儿工做的时候,效率提升了,但同时社会资源开销也大了,并且老板还要拿出一笔很大的费用来支付工人的工做,要否则老板估计的日子很差过!从这里咱们做以下结论:app
单线程:优势是简单、占用资源少,缺点就是不能有效利用资源,好比多核cpu等,不适用那种复杂的业务场景dom
多线程:优势是快速、充分利用资源,缺点是资源消耗大,容易形成让你的电脑死机等!异步
这样理解是否是就能很好理解线程了呢, 其实现实中不少道理是想通的 !
为何浏览器是单线程的?
若是须要回答这个问题,咱们首先须要知道浏览器的工做原理!浏览器在获得你的html以后,解析文档首先获得dom tree,而后解析css获得render tree。最后在经过js实现控制页面的显示、交互等!相似这种工做步骤就注定了浏览器里面的js不能是多线程的,为何呢!试想一下若是是多线程的话:浏览器一边解析获得dom tree,一边又解析获得css tree,而后绘制页面,假如此时dom tree尚未彻底解析获得,或者css tree没有彻底解析完成,那么此时绘制页面会不会乱套?又或是一个尚未绘制完成的元素用实现某些逻辑会不会有问题?这些都是限制浏览器中js不能使用多线程的缘由!
浏览器经过事件队列来实现处理异步逻辑
浏览器中js确实是单线程的,可是浏览器是怎么响应某个按钮的点击处理或者在未来某个时间执行特定脚本呢!这里就要提到时间队列了,这是个什么概念呢!按我本身的理解就是:浏览器给脚本又单独分配了一个线程,这里确实是多线程!只不过是浏览器宿主环境提供的,用来处理未来js中异步须要执行的代码或处理某些事件的回调!为何叫队列呢!人家都叫队列了,说明这些异步脚本不是随便放进去的,而是按照一种方式有序地排在里面的!看下面的代码:
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>demo</title>
</head>
<body>
<button id="demo">点击</button>
<script type="text/javascript">
window.onload = function(){
var btn = document.getElementById('demo');
setTimeout(function(){
console.log('10以后');
},10000);
setTimeout(function(){
console.log('5秒以后');
},5000);
btn.onclick = function(){
console.log('btn');
};//咱们在打开页面大概5秒到10秒的时候点击button(说明一下,并非准确的,只是方便举例)
//咱们发现先打印‘5秒以后’,再打印‘btn’,最后打印‘10秒以后’
};
</script>
</body>
</html>
上面的打印顺序说明异步代码是一种顺序(先来后到)在事件队列里面排序的!未来触发事件的时候老是以这样一个顺序去查找相应的代码而后执行之!
从单线程的角度出发怎么提高浏览器性能
前面的分析得知,单线程就是一我的在工做,因此你就不要一会儿给不少工做给他作,好比给他原本是多我的(多线程)作的工做,这样即时能作,估计浏览器也会很卡,或者假死或卡死!实际上页面场景中这样的工做包括更新dom、监听某种滚动、resize事件回调处理等!咱们来看一个例子,咱们须要用是js实现渲染一个2万行6列的表格,咱们首先不作代码优化,看下效果会怎么样,代码以下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>渲染table测试</title>
<style type="text/css">
*{
margin: 0px;
padding: 0px;
}
.container{
width: 1000px;
margin: 0px auto;
font-size: 14px;
text-align: center;
color: #000;
}
table{
border-spacing: 0px;
width: 100%;
line-height: 1.5em;
}
</style>
</head>
<body>
<div id="box" class="container"></div>
<script type="text/javascript">
window.onload = function(){
var boxDom = document.getElementById('box');
var cTable = document.createElement('table');
var index = 0;//单元格索引,从0开始
for(var i = 0;i<20000;i++){
var tr = document.createElement('tr');
for(var j= 0;j<6;j++){
var td = document.createElement('td');
td.innerHTML = index;
tr.appendChild(td);
index++;
};
cTable.appendChild(tr);
};
boxDom.appendChild(cTable);
};
</script>
</body>
</html>
咱们在浏览器中看到实际效果:浏览器等待了一段时间才渲染出来,在此期间浏览器像是被卡主了同样什么都不能作!好,咱们对上面的代码作一个优化,使其分几步完成上面的需求,这样每次要作的事情就不会太多了!代码以下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>渲染table测试</title>
<style type="text/css">
*{
margin: 0px;
padding: 0px;
}
.container{
width: 1000px;
margin: 0px auto;
font-size: 14px;
text-align: center;
color: #000;
}
table{
border-spacing: 0px;
width: 100%;
line-height: 1.5em;
}
</style>
</head>
<body>
<div id="box" class="container"></div>
<script type="text/javascript">
window.onload = function(){
var boxDom = document.getElementById('box');
var cTable = document.createElement('table');
var index = 0;//单元格索引,从0开始
//此次咱们分5步来完成上面的任务,其实也能够分更多步
var oneStepNum = 4000;
var currentStep = 1;
var renderTable = function renderTable(){
for(var i = 0;i<oneStepNum;i++){
var tr = document.createElement('tr');
for(var j = 0;j<6;j++){
var td = document.createElement('td');
td.innerHTML = index;
tr.appendChild(td);
index++;
};
cTable.appendChild(tr);
};
boxDom.appendChild(cTable);
currentStep++;
if(currentStep>5){
clearTimeout(timer);
return;
};
var timer = setTimeout(renderTable,0);
};
renderTable();//渲染dom
};
</script>
</body>
</html>
咱们发现代码通过这样优化以后,是否是dom很快就被渲染出来了呢!其思想就是:把一个很耗性能的操做或长时间的操做分解成一些耗性能较少或这耗时少的操做,并善于利用setTimeout用来分解一些操做,就不会形成耗时长的代码使浏览器卡死的状况了!并且能快速的把页面呈如今用户面前!
其实不少优化代码都是采用这种原理作的,好比防抖等!