【JavaScript基礎】Js的定時器(你想看的原理也在喲)

歸子莫發表於2021-11-01

【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(),裡面的回撥函式就是非同步程式碼,但是這裡面的程式碼不會立馬執行,而是要等待主佇列為空,並達到定的延時時間才會執行。

執行機制

setTimeoutsetInterval的執行機制是,將指定的程式碼移出本次執行,等到下一輪Event Loop時,再檢查是否到了指定時間。如果到了,就執行對應的程式碼;如果不到,就等到再下一輪Event Loop時重新判斷。

這意味著,setTimeoutsetInterval指定的程式碼,必須等到本輪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)回撥函式執行時間很長(長於定義的間隔時間)的話,間隔定時器有可能無間隔的持續執行。

感謝

萬能的網路

菜鳥教程

以及勤勞的自己,個人部落格GitHub測試GitHub

公眾號-歸子莫,小程式-小歸部落格

相關文章