關於SetTimeout性能的問題

前言
以前有寫過關於瀏覽器中關於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用來分解一些操做,就不會形成耗時長的代碼使瀏覽器卡死的狀況了!並且能快速的把頁面呈如今用戶面前!

其實不少優化代碼都是採用這種原理作的,好比防抖等!

相關文章
相關標籤/搜索