2017年4月27日星期四 07:44:51
在這邊要紀錄一個重要的概念,那就是當我們必須在Client端執行一個JavaSctipt運算時,如果該運算是一個耗時的工作(比方說一個超大迴圈),那麼由於Javascript是單一執行緒的工作特性,所以一旦執行這個運算工作,user勢必得等到該運算完成才能看到網頁最新狀態,這會造成使用者的頁面render卡頓現象,因為在運算完成之前,頁面無法做其他的事。
所以為解決這個問題,我們可以做的一個方式便是把該大型Javascript運算利用setTimeout方式將其分割成多個小task來批次處理,如此在每個小task執行完成空檔,Client Browser便有空餘時間先插入安排其他非同步工作(如:MouseOnClick),完成後再繼續下一個task,如此網頁的Render便比較不會有卡頓現象,這是一個Javascript或Web開發時很重要的任務分割以提昇效能的概念,但前提是在類單執行緒的作業環境,在多執行緒環境下,其實這問題是比較小的。
以下便提供一個我實作自"Secrets of the Javascript Ninja"書本的範例,這個程式是要在使用者頁面用Javascript產生一個有12000 Rows * 6 Cells 的大型表格。 想當然爾傳統做法一定是寫一個簡單的雙迴圈先產生12000 Rows再在每一個產生row的過程建立6個cell,但是因為量實在太大,在Javascript產生出這個大表格之前,使用者大概便已經砸了電腦了。
所以這個範例講會套用前面提到分割任務的概念,先分成6個小task,再依序產生表格內容:
- Task 1 : Row 0 – 1999
- Task 2 : Row 2000 – 3999
- Task 3 : Row 4000 – 5999
- Task 4 : Row 6000 – 7999
- Task 5 : Row 8000 – 9999
- Task 6 : Row 10000 – 11999
我想這是寫Web時要考慮到的重要概念,以下是程式碼與執行結果,大家有興趣可以試一試用傳統做法或把tasks 變數指定為 1時對於render過程是不是會有很大差異,程式細節我就不多講囉。
DivideJavascriptBigTtask.html
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Divide Javascript big task to improve the render efficiency</title> </head> <body> <h2>Divide Javascript big task to improve the render efficiency</h2> <!-- 注意: 因為執行Javascript 必須參考到 tbody 元素, 所以必需把table的html結構放在javascript之前,否則會發生參考錯誤. --> <table border=1> <tbody></tbody> </table> <script> var allrows = 12000; //要建立 12000列 var tasks = 6; // 6 cells per row var chunk = allrows/tasks; //one task have to generate chunk rows. var task_number = 0; //Indicate what task is running.
var t = document.getElementsByTagName("tbody")[0];
//use setTimeout() to process all sub tasks. setTimeout( function generateRows() { var start_row = task_number * chunk; for(var i=0; i<chunk; i++) { var tr = document.createElement("tr"); for(var j=0; j<6; j++) { var td = document.createElement("td"); td.appendChild(document.createTextNode("Row " +(i+start_row)+ ", cell " + j + ", in task " + (task_number+1))); tr.appendChild(td); } t.appendChild(tr); } task_number++; if(task_number < tasks) setTimeout(generateRows,0); }, 0); </script> </body> </html> |
Executed Result: