【JavaScript基礎】Js的定時器(你想看的原理也在喲)
部落格說明
文章所涉及的資料來自網際網路整理和個人總結,意在於個人學習和經驗彙總,如有什麼地方侵權,請聯絡本人刪除,謝謝!
說明
本章是經歷第二次翻新,時過一年,再看自己的文章,覺得需要做點什麼,它得豐富一點!篇幅半頁或者一頁,自己都感覺有點對不住自己。為了對得住自己,加了原理解析和案例。知其然與所以然。
Js的定時器,是前端的基本工具,在日常的開發和工作上也會經常的使用到。前端的定時器有兩種,一種是一次性定時器,一種是重複性定時器。
一次性定時器setTimeout
標準:在指定的毫秒數後呼叫函式或計算表示式。
口語:使一段程式碼在指定時間後執行。
語法
setTimeout(code,millisec,lang)
引數 | 描述 |
---|---|
code | 必需。要呼叫的函式後要執行的 JavaScript 程式碼串。 |
millisec | 必需。在執行程式碼前需等待的毫秒數。 |
lang | 可選。指令碼語言可以是:JScript \ VBScript \ JavaScript |
案例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Js的定時器</title>
</head>
<body>
<p>點選按鈕,在等待 3 秒後彈出 "Hello"。</p>
<button onclick="myFunction()">我是按鈕,點我</button>
<script>
function myFunction() {
setTimeout(function () {
alert("Hello")
}, 1000 * 3);
}
</script>
</body>
</html>
重複性定時器setInterval
標準:按照指定的週期(以毫秒計)來呼叫函式或計算表示式。方法會不停地呼叫函式,直到 clearInterval() 被呼叫或視窗被關閉。
口語:可以使一段程式碼每過指定時間就執行一次。
語法
setInterval(code,millisec,lang)
引數 | 描述 |
---|---|
code | 必需。要呼叫的函式或要執行的程式碼串。 |
millisec | 必須。週期性執行或呼叫 code 之間的時間間隔,以毫秒計。 |
lang | 可選。 JScript \ VBScript \ JavaScript |
案例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Js的定時器</title>
</head>
<body>
<input id="clock" />
<button onclick="clearDate()">別變了,快停止吧,點我!</button>
<script type="text/javascript">
const id = setInterval(() => {
const date = new Date();
const time = date.toLocaleTimeString();
document.getElementById("clock").value = time;
}, 1000);
function clearDate() {
clearInterval(id)
}
</script>
</body>
</html>
時間的誤差
setInterval指定的是<span style="color: red">開始執行</span>之間的間隔,並不考慮每次任務執行本身所消耗的時間。因此實際上,兩次執行之間的間隔會小於指定的時間。
比如,setInterval指定每100ms執行一次,每次執行需要5ms,那麼第一次執行結束後95毫秒,第二次執行就會開始。如果某次執行耗時特別長,比如需要105毫秒,那麼它結束後,下一次執行就會立即開始。
為了確保兩次執行之間有固定的間隔,可以不用setInterval,而是每次執行結束後,使用setTimeout指定下一次執行的具體時間。
var i = 1;
var timer = setTimeout(function() {
alert(i++);
timer = setTimeout(arguments.callee, 2000);
}, 2000);
消除定時器
在使用定時器的時候,需要有一個好的習慣,那就是清除定時器,特別是對於重複型定時器,一定要及時清除。
定時器清除的方法
相對於兩種建立定時器的方法,Js也給出了相對應的清除方法,分別是clearTimeout(obj)
和clearInterval(obj)
。
在看到這兩種方法都是接收一個引數,這個引數就是定時器的標識,這個標識在使用定時器的時候被定義用來接收定時器方法的變數。
案例
// 一秒之後列印
const test1 = setTimeout(function(){
console.log('hello world')
},1000);
// 每秒列印一次
const test2 = setInterval(function(){
console.log('hello world')
},1000)
// 清理定時器
clearTimeout(test1);
clearInterval(test2)
原理
JavaScript語言特性
JavaScript是一門基於物件的弱型別語言,它作為瀏覽器指令碼語言,主要用途是負責與頁面的互動,以及操作DOM。
重點來了,JavaScript的執行環境是單執行緒的,即預設情況下是同步載入的,也就是說 JavaScript的載入是阻塞的。在同一時間內JavaScript只能完成一件事,自上而下執行,下面的程式碼等待上面的程式碼解析完成。
在這種情況下,後面的程式碼其實就是被阻塞了。阻塞就意味著等待,等待就意味著使用者體驗,使用者體驗一來,那必須得使勁想辦法,所以同步和非同步出現了。
同步和非同步
同步操作:佇列執行。
非同步操作:併線執行。
非同步的任務不具有阻塞效應。
同步任務都是在主執行緒中執行,形成了一個執行棧,直到主執行緒空閒時,才會去事件佇列中檢視是否有可執行的非同步任務,如果有就推入主程式中。
非同步任務在JavaScript中是通過回撥函式實現非同步的,回到本文的主題,一旦使用了setTimeout(),裡面的回撥函式就是非同步程式碼,但是這裡面的程式碼不會立馬執行,而是要等待主佇列為空,並達到定的延時時間才會執行。
執行機制
setTimeout
和setInterval
的執行機制是,將指定的程式碼移出本次執行,等到下一輪Event Loop時,再檢查是否到了指定時間。如果到了,就執行對應的程式碼;如果不到,就等到再下一輪Event Loop時重新判斷。
這意味著,setTimeout
和setInterval
指定的程式碼,必須等到本輪Event Loop的所有同步任務都執行完,再等到本輪Event Loop的“任務佇列”的所有任務執行完,才會開始執行。由於前面的任務到底需要多少時間執行完,是不確定的,所以沒有辦法保證在時間內執行。
案例
setTimeout(function() {
console.log("非同步任務執行");
}, 0);
function a(x) {
console.log("a() 開始執行");
b(x);
console.log("a() 結束執行");
}
function b(y) {
console.log("b() 開始執行");
console.log(y);
console.log("b() 結束執行");
}
console.log("同步任務開始");
a("hello world");
console.log("同步任務結束");
// 同步任務開始
// a() 開始執行
// b() 開始執行
// hello world
// b() 結束執行
// a() 結束執行
// 同步任務結束
// 非同步任務執行
總結
- JavaScript引擎是單執行緒,它會強制非同步事件排隊等待執行;
- setTimeout和setInterval執行原理是不一樣的,需要注意他們的執行時間的影響;
- 如果一個一次性定時器(setTimeout)被阻塞了,它會等待直到有合適的執行時間(等待時間有可能比它定義的延遲時間長);
- 如果重複性定時器(setInterval)回撥函式執行時間很長(長於定義的間隔時間)的話,間隔定時器有可能無間隔的持續執行。
感謝
萬能的網路
公眾號-歸子莫,小程式-小歸部落格