1、經過定時器咱們能夠間隔設定時間重複調用某個函數,利用這個特性,咱們能夠作不少事,例如,12306上的每間隔5秒查詢自動查詢一次餘票,簡單動畫的實現等等html
2、定時器的格式:瀏覽器
定時器有兩種格式,分別是setInterval(func, time) 和 setTimeout(func, time),這兩個有一些區別函數
一、setInterval(func, tine);動畫
(1)、 此定時器操做是這樣的,解釋到該語句時,是要間隔time時間後第一次執行func函數,間隔time時間後再次執行func函數,重複上述...spa
來個demo理解一下吧:線程
var count = 0; var print = function () { console.log(count++); } // 每間隔1秒調用一次print函數,因此帶引結果應該是從0,1,2...一直打印 setInterval(print, 5000);
P.S. 咱們在setInterval()中傳遞函數時,咱們通常用個匿名函數包裹一下,而後再匿名函數中執行咱們要調用的函數,這樣咱們能夠在匿名函數中進行更多操做,更靈活一些,以下code
var count = 0; var print = function () { console.log(count++); } // 每間隔1秒調用一次print函數,因此帶引結果應該是從0,1,2...一直打印 setInterval(function () { print(); }, 1000);
(2)、如何中止計時器?htm
setInterval有個返回值,咱們獲取其返回值後,調用clearInterval(返回值), 便可中止計時器,以下demoblog
var count = 0; // 間隔1s打印一次cunt,當count爲5時結束定時器,因此打印結果爲 0, 1, 2, 3, 4 var timer = setInterval(function () { count === 5? clearInterval(timer) : console.log(count++); }, 1000);
二、setTimeout()遞歸
(1)、這個和上邊那個定時器用法同樣,都是傳函數和時間間隔,但運行時操做是不一樣,這個是間隔設定時間後,調用咱們傳入的函數,而後就結束了,因此和上邊那個區別在於上邊那個是屢次,而這個只有一次。
// 間隔1s後帶引setTimeout, 因此打印結果就是setTimeout setTimeout(function () { console.log('setTimeout'); }, 1000);
(2)、中止計時器,setTimeout()也有返回值,咱們獲取返回值,而後執行clearTimeout(返回值),便可中止setTimeout()計時器.
(3)、利用setTimeout()來模擬setInterval()
原理很簡單,就是遞歸,在函數內不斷的中止計時器,再添加計時器,達到重複的目的,很少說,直接上demo:
var count = 0; var timer = null; function print() { // 每次添加定時器前,移除前一個定時器 clearTimeout(timer); //函數執行的語句 console.log(count++);
// 添加定時器
timer = setTimeout(function () {
print(); }, 1000);
// 循環結束條件 if(count == 10) { clearTimeout(timer); }
}
timer = setTimeout(function () { print(); }, 1000);
由代碼咱們能夠看出打印結果爲0~9,
注意點:
i、咱們調用的函數內首先第一件事就是移除上一個定時器,由於上一個定時器已經沒有做用了,因此要移除,而後再添加新的定時器
ii、在函數內最好在移除完定時器後緊跟着添加定時器,爲何呢?覺得若是咱們遇到這樣一種狀況,定時器調用的函數須要執行很長一段時間,而後咱們在函數最後添加定時器,最終就形成了這樣一種結果,那就是間隔時間變爲 (函數執行時間 + 定時器間隔時間)極大的延遲了間隔時間,這確定不是咱們想要的。
那爲何添加到函數前面就沒這種狀況了,由於在函數執行後首先就是移除上一個計時器,而後添加新的計時器,這時計時器就開始計時了,而函數正在主線程中被執行,通過計時器設定間隔時間後,將新添加的計時器函數添加到js執行隊列中,等主線程的函數執行完畢後,主線程空閒,則直接執行js執行隊列中等待的計時器函數。這就大大減小了函數執行時間的影響,由於定時器間隔計時和函數執行時間同時進行了,而不是函數執行完再進行計時器的間隔,這就使間隔時間儘可能的接近計時器設定的間隔時間。
3、計時器執行原理和單線程:
一、咱們都知道js語言是單線程的,那單線程是什麼意思呢?那就是‘JavaScrip引擎是單線程運行的,瀏覽器不管何時都有且只有一個主線程在執行JavaScript程序’
二、js執行隊列:因爲js是運行在單線程環境的,因此線程一直是執行者同一個任務,而咱們是講各類欲執行的任務隨機添加到js執行上,當主線程空閒時就會當即執行當前隊首的任務。
也能夠這樣理解,js執行隊列是不少有着各自事務要辦的人排成的長隊,而線程是一個能夠處理各類事務的窗口,可是該窗口一次只能辦理一我的的事務,在窗口處理完當前人的事務後,窗口就處於空閒狀態,而後接着辦理長隊隊首人的事務,就這樣按照長隊順序,窗口一次處理一我的的事務。而咱們所見的代碼執行僅僅是將該代碼事務隨機插入到js執行隊列中排隊等待而已,但由於瀏覽器js引擎處理事務很是快,因此給咱們當即執行不用排隊等待的感受。(注意:往js執行隊列插入任務時是隨機插入到隊列中的,並不必定是要插入到隊尾)
三、計時器工做方式:咱們添加一個定時器後,在間隔咱們設定時間後,將函數代碼隨機插入到js執行隊列中,等主線程空閒時且該函數代碼位於隊首,纔會執行函數代碼。
四、總結來講,就是在JavaScript中沒有任何代碼是當即執行的,只能說一旦主線程空閒則儘快執行
五、setInteral帶來的問題:
(1)、假設使用重複定時器setInterval()定時器,而後添加的函數在線程須要執行很長時間,而重複定時器設定的間隔時間遠遠小於該函數執行時間,則在該函數執行期間,該定時器代碼會被屢次添加到js執行隊列等待,這就致使第一次函數執行結束後以後,以前屢次添加的函數代碼會連續執行而沒有間隔,而且第一個函數開始執行須要等主線程空閒才行,因此其第一次的間隔時間也不必定是咱們設定的。(由於在上一個函數執行時多個函數代碼已經添加到js執行隊列中,因此函數執行結束後會馬上執行已經添加的函數代碼)
(2)、鑑於上述問題,js引擎設定了這樣一條規則:當使用setInterval()時,僅當js執行隊列中沒有該定時器的任何其餘代碼實例時,才能將該定時器代碼添加到js執行隊列中。不然就是加入失敗,從新計時。
但此規則又帶來了其餘問題;
i、某些間隔會被跳過
ii、多個定時器代碼執行之間的間隔可能會比預期的小,實際咱們等待間隔大於等於咱們設定的間隔時間。
(3)因此在使用setInterval()作動畫時要注意兩個問題:
i、不能使用固定步長做爲作動畫,必定要使用百分比: 開始值 + (目標值 - 開始值) * (Date.now() - 開始時間)/ 時間區間
ii、若是主進程運行時間過長,會出現跳幀的現象(由於js引擎的這條規則而沒法添加函數代碼)
(4)、解決:
使用鏈式setTimeout(),如上演示的用setTimeout()來模擬setInterval(),即每次在函數體執行結束的最後添加延時定時器,使函數執行之間的間隔最小爲咱們設定的間隔時間。
第三項參考來源:http://www.cnblogs.com/dojo-lzz/p/4606448.html
------------------------------------------------------------------------------------------------------end