title: 高級定時器
date: 2016-12-13
tag: JS高級技巧css
首先,JavaScript 中沒有代碼是當即執行的,而是一旦進程空閒則當即執行。html
進程什麼時候空閒,取決於上一個執行隊列的執行時間,而與此對應的是隨着頁面中生命週期的推移而產生的代碼執行順序隊列。數組
定時器對隊列的工做方式是,當設定的時間過去之後將代碼插入隊列,但不表明代碼會被當即執行。瀏覽器
不少狀況下,咱們都須要使用 setInterval() 重複的執行同一段代碼去作同一件事情,而在這時,最大的問題在於定時器可能在代碼再次被添加到隊列以前尚未被執行完成,從而致使某些間隔被跳過或者多個定時器的代碼執行時間間隔被縮短。異步
爲了不以上缺點,可使用鏈式調用 setTimeout() 模式函數
setTimeout(function(){ // do something setTimeout(arguments.callee, interval); }, interval)
一個例子:code
setTimeout(function(){ $("#block").css({ 'left': $('#block').position().left -1, }) if($('#block').position().left > 0){ setTimeout(arguments.callee, 30); } }, 30)
爲了防止惡意程序猿將用戶的計算機搞掛,瀏覽器對 JavaScript 可以使用的資源進行了限制,若是代碼的運行時間超過特定時間或者特定語句數量就不讓其繼續運行。htm
而腳本運行時間過長的兩個主要緣由是:1)過長,過深嵌套的函數調用;2)進行大量處理的循環。對象
針對第二種問題,使用定時器是解決方法之一。使用定時器分隔循環,是一種叫做 數組分塊(array chunking) 的技術。生命週期
在數組分塊模式中,array 變量本質上就是一個 「代辦事項」 列表,它包含了要處理的項目,而 shift() 能夠獲取隊列中下一個要處理的項目,而後將其傳遞個某個函數。當隊列中還剩下其它項目時,則設置另外一個定時器,並經過 arguments.callee 調用同一個匿名函數。
function chunk(array, process, context){ setTimeout(function(){ var item = array.shift() process.call(context, item) if(array.length > 0){ setTimeout(arguments.callee, 100) } }, 100) }
chunk() 方法接收三個參數: 要處理項目的數組,用於處理項目的函數,可選的運行該函數的環境。
在函數內部,經過 call() 調用 process() 函數,這樣能夠設置一個合適的執行環境。爲定時器設定的時間間隔使得 JavaScript 進程有時間在處理項目的事件之間轉入空閒。
調用實例:
var data = [12,124,343,56,76767,43,654,34645,56456,767,4645] function printValue(item){ var div = $('#block').html() $('#block').html(div + item + '<br>') } chunk(data, printValue)
如上,函數 printValue()
將 data
數組中的每一個值輸出到一個 div
元素中。因爲函數處於全局做用域中,所以無需給 chunk()
函數傳遞 context
對象。
若是想保持原數組不變,則應將該數組的克隆傳遞給 chunk()
chunk(data.concat(), printValue)
調用某個數組的.contact()
,若是不傳遞任何參數,將返回和原來數組中項目同樣的數組。
函數節流 的基本思想是指,某些代碼不能夠在沒有間斷的狀況下連續重複的執行。
瀏覽器中某些計算和處理的代價要比其餘的昂貴不少,好比,DOM 操做比非 DOM 交互須要更多的內存和 CPI 時間,而進行過多的 DOM 相關操做可能致使瀏覽器掛起甚至崩潰,對於這種問題,可使用定時器對函數進行節流。
函數節流的基本模式能夠簡化以下:
function throttle(method, context){ clearTimeout(method.tId) method.tId = setTimeout(function(){ method.call(context) }, 100) }
throttle() 函數接收兩個參數: 要執行的函數以及在哪一個做用域中執行。該函數首先清除以前設置的任何定時器。定時器 ID 是存儲在函數的 tId
屬性中的,固然,首次將方法傳遞給 throttle 函數可能並不存在該屬性。而後定義一個新的定時器,並將 ID 存儲在 tId 屬性中。而 call() 用來確保方法在適當的環境中執行。若是沒有給出第二個參數,那麼就在全局做用域內執行該方法。
在 setTimeout() 中用到的函數其執行環境老是 window
throttle 方法調用實例:
function resizeDiv(){ var div = document.querySelector("#block") div.style.height = div.offsetWidth + "px" } window.onresize = function(){ throttle(resizeDiv) }
如上,爲了保證在 resize
事件中瀏覽不會進行高頻率,或者屢次計算,咱們給 window.onresize
綁定了一個函數,在該函數調用了 throttle 方法,從而在窗口大小發生改變的時候是 div#block
的高度與其寬度保持一致。