JavaScript - 定時器 - 學習/實踐

1.應用場景

主要用於定時執行代碼.

應用場景:

1.防抖動,

2.調整事件的發生順序, 用戶自定義的回調函數,通常在瀏覽器的默認動作之前觸發, 

3.將任務放到瀏覽器最早可得的空閒時段執行,所以用於那些計算量大、耗時長的任務,

4.代碼高亮的處理

5.實現網頁動畫

6.實現輪詢。

2.學習/操作

1.介紹

JavaScript 提供定時執行代碼的功能,叫做定時器(timer),主要由setTimeout()setInterval()這兩個函數來完成。

它們向任務隊列添加定時任務

 

2.setTimeout

setTimeout函數用來指定某個函數或某段代碼,在多少毫秒之後執行。它返回一個整數,表示定時器的編號,以後可以用來取消這個定時器。

var timerId = setTimeout(func|code, delay);

上面代碼中,setTimeout函數接受兩個參數,第一個參數func|code是將要推遲執行的函數名或者一段代碼,第二個參數delay是推遲執行的毫秒數。

console.log(1);
setTimeout('console.log(2)',1000);  //這裏不論設置多久, 哪怕是0 , 也是後執行 console.lo(2)
console.log(3);
// 1
// 3
// 2

上面代碼會先輸出1和3,然後等待1000毫秒再輸出2。

注意,console.log(2)必須以字符串的形式,作爲setTimeout的參數。//目前Chrome已經不支持這種方式, 因爲認爲不安全 

版本 81.0.4044.138(正式版本) (64 位)

 

如果推遲執行的是函數,就直接將函數名,作爲setTimeout的參數。//推薦寫法

function f() {
  console.log(2);
}

setTimeout(f, 1000);

setTimeout的第二個參數如果省略,則默認爲0。

setTimeout(f)
// 等同於
setTimeout(f, 0)

除了前兩個參數,setTimeout還允許更多的參數。它們將依次傳入推遲執行的函數(回調函數)。

setTimeout(function (a,b) {
  console.log(a + b);
}, 1000, 1, 1);

輸出結果: 3

上面代碼中,setTimeout共有4個參數。最後那兩個參數,將在1000毫秒之後回調函數執行時,作爲回調函數的參數。

還有一個需要注意的地方,

如果回調函數是對象的方法,那麼setTimeout使得方法內部的this關鍵字指向全局環境,而不是定義時所在的那個對象。

var x = 1;

var obj = {
  x: 2,
  y: function () {
    console.log(this.x);
  }
};

setTimeout(obj.y, 1000) // 1

上面代碼輸出的是1,而不是2。

因爲當obj.y在1000毫秒後運行時,this所指向的已經不是obj了,而是全局環境。

 

爲了防止出現這個問題,一種解決方法是將obj.y放入一個函數。

var x = 1;

var obj = {
  x: 2,
  y: function () {
    console.log(this.x);
  }
};

setTimeout(function () {
  obj.y();
}, 1000);
// 2

上面代碼中,obj.y放在一個匿名函數之中,這使得obj.yobj的作用域執行,而不是在全局作用域內執行,所以能夠顯示正確的值。

另一種解決方法是,使用bind方法,將obj.y這個方法綁定在obj上面。

var x = 1;

var obj = {
  x: 2,
  y: function () {
    console.log(this.x);
  }
};

setTimeout(obj.y.bind(obj), 1000)
// 2

3.setInterval

setInterval函數的用法與setTimeout完全一致,區別僅僅在於setInterval指定某個任務每隔一段時間就執行一次,也就是無限次的定時執行。

var i = 1
var timer = setInterval(function() {
  console.log(2);
}, 1000)

上面代碼中,每隔1000毫秒就輸出一個2,會無限運行下去,直到關閉當前窗口。

setTimeout一樣,除了前兩個參數,setInterval方法還可以接受更多的參數,它們會傳入回調函數。

 

下面是一個通過setInterval方法實現網頁動畫的例子。

var div = document.getElementById('someDiv');
var opacity = 1;
var fader = setInterval(function() {
  opacity -= 0.1;
  if (opacity >= 0) {
    div.style.opacity = opacity;
  } else {
    clearInterval(fader);
  }
}, 100);

上面代碼每隔100毫秒,設置一次div元素的透明度,直至其完全透明爲止。

 

setInterval的一個常見用途是實現輪詢。下面是一個輪詢 URL 的 Hash 值是否發生變化的例子。

var hash = window.location.hash;
var hashWatcher = setInterval(function() {
  if (window.location.hash != hash) {
    updatePage();
  }
}, 1000);

Note:

setInterval指定的是“開始執行”之間的間隔,並不考慮每次任務執行本身所消耗的時間。

因此實際上,兩次執行之間的間隔會小於指定的時間。

比如,setInterval指定每 100ms 執行一次,每次執行需要 5ms,那麼第一次執行結束後95毫秒,第二次執行就會開始。

如果某次執行耗時特別長,比如需要105毫秒,那麼它結束後,下一次執行就會立即開始。

 

爲了確保兩次執行之間有固定的間隔,可以不用setInterval,而是每次執行結束後,使用setTimeout指定下一次執行的具體時間

var i = 1;
var timer = setTimeout(function f() {
  // ...
  timer = setTimeout(f, 2000);
}, 2000);

上面代碼可以確保,下一次執行總是在本次執行結束之後的2000毫秒開始。

Note: 上面的code即替換setInterval

 

4.clearTimeout(),clearInterval()

setTimeoutsetInterval函數,都返回一個整數值,表示計數器編號。

var id1 = setTimeout(f, 1000);
var id2 = setInterval(f, 1000);

clearTimeout(id1);
clearInterval(id2);

上面代碼中,回調函數f不會再執行了,因爲兩個定時器都被取消了。

setTimeoutsetInterval返回的整數值是連續的,也就是說,第二個setTimeout方法返回的整數值,將比第一個的整數值大1。

 

Note:  setTimeoutsetInterval 兩者之間返回的ID值是在相互基礎上交叉+1的

截圖:

 

利用這一點,可以寫一個函數,取消當前所有的setTimeout定時器。//TBD

(function() {
  // 每輪事件循環檢查一次
  var gid = setInterval(clearAllTimeouts, 0);

  function clearAllTimeouts() {
    var id = setTimeout(function() {}, 0);
    while (id > 0) {
      if (id !== gid) {
        clearTimeout(id);
      }
      id--;
    }
  }
})();

上面代碼中,先調用setTimeout,得到一個計算器編號,然後把編號比它小的計數器全部取消。

 

5.實例:debounce 函數  //防抖動

https://wangdoc.com/javascript/async/timer.html#實例:debounce-函數

code:


<!DOCTYPE html>
<html>
<head>
    <title>防抖動</title>
    <style type="text/css">
        textarea{
            width: 200px;
            height: 200px;
        }
    </style>
</head>
<body>
<div id="someDiv">hello world</div>
<textarea></textarea>

<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
(function(){
    //console.log(window);
    //var dom = $('#someDiv');
    //console.log(dom);
    //return;
    $('textarea').on('keydown', debounce(ajaxAction, 2500)); //防抖動間隔爲2.5s  可以將數值改爲0 效果更佳明顯

    function debounce(fn, delay){
      var timer = null; // 聲明計時器
      return function() {
        var context = this;
        var args = arguments;
        // console.log(args)
        clearTimeout(timer);
        timer = setTimeout(function () {
          fn.apply(context, args);
        }, delay);
      };
    }

    function ajaxAction(para){
        console.log(para, 'ajaxAction');
    }
})()

</script>
</body>
</html>

6.運行機制  

https://wangdoc.com/javascript/async/timer.html#運行機制

 

7.setTimeout(f, 0)   //應用場景有一些, 建議練習一下

https://wangdoc.com/javascript/async/timer.html#settimeoutf-0

code:


<!DOCTYPE html>
<html>
<head>
    <title>可以調整事件的發生順序/用戶自定義的回調函數,通常在瀏覽器的默認動作之前觸發</title>
    <style type="text/css">
        textarea{
            width: 200px;
            height: 200px;
        }
    </style>
</head>
<body>
<div id="someDiv">hello world</div>
<input type="button" id="myButton" value="click">
<input type="text" id="input-box">

<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
(function(){

    //1.可以調整事件的發生順序[這裏目標是先執行父級元素的事件,然後執行子元素的事件]
    // var input = document.getElementById('myButton');

    // input.onclick = function A() {
    //   setTimeout(function B() {
    //     input.value +=' input';
    //   }, 0)
    // };

    // document.body.onclick = function C() {
    //   input.value += ' body'  //先執行父級元素的事件
    // };


    //2.用戶自定義的回調函數,通常在瀏覽器的默認動作之前觸發  -- 想在用戶每次輸入文本後,立即將字符轉爲大寫
    document.getElementById('input-box').onkeypress = function (event) {
      //this.value = this.value.toUpperCase();  //這種寫法, 只能讓前面輸入的字母大寫, 最後一個可能還是輸入時的樣子

      //改寫如下:
      var self = this;
      setTimeout(function() {
        self.value = self.value.toUpperCase();
      }, 0);
    }

    //3.那些計算量大、耗時長的任務,常常會被放到幾個小部分,分別放到setTimeout(f, 0)裏面執行
    var div = document.getElementsByTagName('div')[0];
    // console.log(div);

    // 寫法一 //會看到阻塞 瀏覽器刷新圖標一直在打轉
    // for (var i = 0xFFFFFF; i > 0xA00000; i--) {  
    //   div.style.backgroundColor = '#' + i.toString(16); //可以轉換爲 rgb(160, 0, 1)形式
    // }


    // 寫法二  //會看到沒有阻塞 瀏覽器刷新圖標沒有打轉, 而且可以看到頁面上顏色的漸變效果
    var timer;
    var i=0x100000;

    function func() {
      timer = setTimeout(func, 0);
      div.style.backgroundColor = '#' + i.toString(16);
      if (i++ == 0xFFFFFF) clearTimeout(timer);
    }

    timer = setTimeout(func, 0);


    //4.另一個使用這種技巧的例子是代碼高亮的處理。如果代碼塊很大,一次性處理,可能會對性能造成很大的壓力,那麼將其分成一個個小塊,一次處理一塊,比如寫成setTimeout(highlightNext, 50)的樣子,性能壓力就會減輕。

})()

</script>
</body>
</html>

後續補充

...

3.問題/補充

TBD

4.參考

https://wangdoc.com/javascript/async/timer.html  //阮一峯 - 定時器 

後續補充

...

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章