setTimeout和setInterval的區別以及如何寫出效率高的倒數計時

桔子_Lynn發表於2016-12-26

1.setTimeout和setInterval都屬於js中的定時器,可以規定延遲時間再執行某個操作,不同的是setTimeout在規定時間後執行完某個操作就停止了,而setInterval則可以一直迴圈執行下去。

  下面介紹一下兩種定時器的語法:

  setTimeout(expression,milliseconds)  以及 setInterval(expression,milliseconds)

  其中 expression是將要執行的某一項操作,而milliseconds則是延遲時間,expression可以是一個將要執行的函式名,也可以是一個字串

  比如:

1 function fun(){
2   alert('hello');
3 }
4 setTimeout(fun,1000);   //引數是函式名
5 setTimeout('fun()',1000);  //引數是字串
6 setInterval(fun,1000);
7 setInterval('fun()',1000);

 在上面程式碼中,無論是setTimeout還是setInterval,在使用函式名作為呼叫控制程式碼時不能帶引數,使用字串呼叫時可以帶引數,如:setTimeout('fun(name)',1000);

 但是有些場合會要求必須使用函式名作為呼叫控制程式碼,此時可以用另一種方法帶引數:

 方法一:

function fun(name){
  alert('hello'+' ' +name);
}
function _fun(name){
  return function(){
     fun(name);
  }
}
setTimeout(_fun('Tom'),1000);   //引數是函式名

方法二:

不用單獨再定義一個函式,直接將函式呼叫放在一個函式裡面,可以使用函式名作為呼叫控制程式碼:

function fun(name){
  alert('hello'+' ' +name);
}
setTimeout(function(){
  fun('Tom');
},1000);   //引數是函式名

 setInterval使用方式一樣,就不再多寫了。

 在以上程式碼中,setTimeout和setInterval的區別就是 setTimeout延遲一秒彈出'hello',之後便不再執行;而setInterval則會每隔一秒鐘彈出'hello',直至用clear來清除

 清除定時器的語法:

 定時器會返回一個id,只要對這個id進行清除的操作即可:

1 function fun(){
2   alert('hello');
3 }
4 var t1 = setTimeout(fun,1000);
5 var t2 = setInterval(fun,1000);
6 clearTimeout(t1);
7 clearInterval(t2);

2. 用setTimeout實現setInterval的功能

  雖然setTimeout只在延時時間後執行一次,但是我們可以使用遞迴呼叫的方法實現迴圈呼叫,實現類似setInterval的功能,例:

1 function fun(){
2   alert('hello');
3   setTimeout(fun,1000);
4 }
5 fun();

 以上程式碼的功能是每隔一秒鐘頁面便彈出“hello”,相當於setInterval的迴圈間隔呼叫。

 但是即使可以實現同樣的功能,兩種定時器也還是有執行方面的差別:

  比如延時1s執行函式fun(),但是函式內部的操作執行需要的時間是2S,由於JavaScript是單執行緒的,那在這次執行完之前,下一次的迴圈便被阻塞了,處在排隊的狀態,當此次操作執行完以後才會執行處在排隊狀態的操作,

  但是排隊的序列太多,阻塞結束以後只能執行一個,這樣會造成效能的浪費,

  所以在這樣的情況下,使用setTimeout遞迴呼叫實現迴圈的方法便顯得很方便,它不會發生阻塞的狀況

  比如函式內部的執行需要2S,那setTimeout會等2S執行完以後才去遞迴呼叫,也就是說整個一次迴圈需要3S的時間。

  但是setTimeout又不如setInterval執行的精確,所以在不同情況下可以選擇不同的定時器以達到最好的效果。

3.如何寫出一個高效的倒數計時

  身為小白的我目前能寫出來的只是能實現該需求的最低階的程式碼OTZ

 1 var t,num = 60;
 2 function timeDown(){
 3   num--;
 4   num = num < 10 ? ('0'+num) : (num+'');     // 左側補0操作
 5   document.getElementById('demo').innerHTML = num;
 6   t = setTimeout(function(){
 7     timeDown();
 8   },1000);
 9   if(num <= 0){
10     clearTimeout(t);
11   }
12 }
13 timeDown();

以上程式碼是用setTimeout遞迴呼叫外層函式實現的

下面是用setInterval實現的方法:

 1 var t,num = 5;
 2 function timeDown(){
 3   num--;
 4   num = num < 10 ? ('0'+num) : (num+'');
 5   document.getElementById('demo').innerHTML = num;
 6   if(num <= 0){
 7     window.clearInterval(t);
 8   }
 9 }
10 t = setInterval(timeDown,1000);

 以上用兩種定時器寫出的程式碼都是運用的最基本的實現方式,也是我現在的經驗和水平能寫出來的,

 前段時間在掘金社群看到一個很厲害的由淺入深的寫倒數計時的方法,現在貼上過來跟我的對比一下,簡直無地自容OTZ

 首先是左側補0的幾種方法:

//方法一:大於10的返回值的型別是number型別,沒有做轉換
function leftPad(i){
    if(i<10){
        i = "0" + i;
    }
    return i;
}
//方法二:對上一種方法做了彌補,但是沒有考慮到複數的情況
function leftPad(i){
  return i < 10 ? '0'+i : i+'';
}
//方法三:對返回值型別做了轉換,同時也考慮到了負數的情況,這也是目前我的水平能想到的寫法了。。
function leftPad(n){
    var n = parseInt(n, 10);
    return n > 0 ? n <= 9 ? ('0'+n) : (n+'') :'00';
}
//下面兩種方法我其實就有點看不懂了,不過還是先貼在這裡,等我學會了再回來看看
function leftPad(n, len){
    len = len || 2;
    n = n + '';
    var diff = len - n.length;
    if (diff > 0) {
        n = new Array(diff + 1).join('0') + n;
    }
    return n;
}
function leftpad (str, len, ch) {
  str = String(str);
  var i = -1;
  if (!ch && ch !== 0) ch = ' ';
  len = len - str.length;
  while (++i < len) {
    str = ch + str;
  }
  return str;
}

 然後是倒數計時的程式碼:

 方法一:

 單例模式,簡單方便好理解,缺點是每次init都會拿一個新定時器,效能不好。繼承和擴充套件能力一般,無法獲取例項屬性,導致了執行狀態都是不可見的。

var CountDown = {
    $ : function(id){/*id選擇器*/},
    init :function(startTime,endTime,el){/*執行定時器入口,使用setTimeout呼叫_timer*/},
    _timer : function(startTime,endTime,el){/*私有方法,處理時間引數等具體業務*/}
}
CountDown.init("","2016,04,23 9:34:44","countdown1");

 方法二:

 標準的原型構造器寫法,簡單方便好理解,確定是每次都拿一個新定時器,例項增多後效能同樣不好,按道理setTime,leftPad等方法都可以通過繼承來實現,方便擴充套件和複用,

 prototype上的方法均為輔助方法,按理不應該被外部呼叫,這裡應該封裝為私有方法或者字首+_,優點可以通過例項拿到相關倒數計時屬性,可以對例項再做擴充套件操作。

function Countdown(elem, startTime, endTime) {
    this.elem = elem;
    this.startTime = (new Date(startTime).getTime()) ? (new Date(startTime).getTime()) : (new Date().getTime());
    this.endTime = new Date(endTime).getTime();
}
Countdown.prototype = {
    SetTime: function() {},
    leftPad: function(n) {},
    DownTime: function() {}
}
var test = new Countdown("time", "2016/1/30,12:20:12", "2017/1/30,12:20:12");
test.SetTime();

 方法三:

 優點:這裡的countdown是一個比較簡單的工廠模式實現,實現了一個統一的create方法,create方法上呼叫了style這個屬性上擴充套件的樣式(style1-3)實現,

 create方法返回的是一個獨立的新例項,並統一擴充套件了go方法,go方法裡統一建立定時器並掛載到timer屬性,在這裡我們也就等同擁有了修改和控制每個工廠造出來的單例的能力,

 樣式做到了可擴充套件,leftPad,timeToSecond也可以方便通過一個utils物件來進行繼承。

 缺點:沒有考慮到上面提到的setTimeout和setInterval的區別,也沒有時間校驗機制,在效能方面考慮不多。

var countdown = {};
countdown.leftPad = function(n, len) {};
countdown.timeToSecond = function(t) {};
/**
 * 倒數計時工廠
 * @param  {[object]} obj 倒數計時配置資訊
 * @return {[object]}     返回一個倒數計時物件
 */
countdown.create = function(obj) {
    var o = {};
    o.dom = document.getElementById(obj.id);
    o.startMS = +new Date(obj.startTime || 0);
    o.endMS = +new Date(obj.endTime || 0);
    obj.totalTime && (o.totalTime = countdown.timeToSecond(obj.totalTime));

    var newCountdown = new countdown.style[obj.style](o);

    newCountdown.go = function(callback) {
        callback && (newCountdown.callback = callback);
        newCountdown.render();
        clearInterval(newCountdown.timer);
        newCountdown.timer = setInterval(newCountdown.render, 1000);
    };
    return newCountdown;
};
countdown.style.style1 = function(obj) {
    this.dom = obj.dom;
    this.startMS = obj.startMS;
    this.endMS = obj.endMS;
    var _this = this;
    this.render = function() {
        var currentMS = +new Date();
        var diff = (_this.endMS - currentMS) / 1000;
        var d = parseInt(diff / 60 / 60 / 24);
        d = countdown.leftPad(d, 3);
        d = d.replace(/(\d)/g, '<span>$1</span>');
        _this.dom.innerHTML = '距離國慶節還有:' + d + '天';
        if (currentMS > _this.endMS) {
            clearInterval(_this.timer);
            if (_this.callback) {
                _this.callback();
            } else {
                _this.dom.innerHTML = '國慶節倒數計時結束';
            }
        }
    };
};
countdown.style.style2 = function(obj) {};
countdown.style.style3 = function(obj) {};
countdown.create({id:"clock3",totalTime:'82:23',style:'style1'}).go(function(){alert('It is over');});

以上是我在社群裡看到的前幾個例子,後面的幾個例子已經是我看不懂的階段了。。。所以就不貼在這裡了,先儲存記錄,然後再慢慢學習

自己的程式設計師之路還有很遠要走。。。

by新手小白的紀錄


  

 

相關文章