<menu id="ycqsw"></menu><nav id="ycqsw"><code id="ycqsw"></code></nav>
<dd id="ycqsw"><menu id="ycqsw"></menu></dd>
  • <nav id="ycqsw"></nav>
    <menu id="ycqsw"><strong id="ycqsw"></strong></menu>
    <xmp id="ycqsw"><nav id="ycqsw"></nav>
  • js的定時器有哪些(定時器的4種寫法及介紹)


    JS提供了一些原生方法來實現延時去執行某一段代碼,下面來簡單介紹一下setTiemout、setInterval、setImmediate、requestAnimationFrame。

    一、什么是定時器

    JS提供了一些原生方法來實現延時去執行某一段代碼,下面來簡單介紹一下 setTimeout: 設置一個定時器,在定時器到期后執行一次函數或代碼段

    var timeoutId = window.setTimeout(func[, delay, param1, param2, ...]);
    var timeoutId = window.setTimeout(code[, delay]);
    

    timeoutId: 定時器IDfunc: 延遲后執行的函數code: 延遲后執行的代碼字符串,不推薦使用原理類似eval()delay: 延遲的時間(單位:毫秒),默認值為0param1,param2: 向延遲函數傳遞而外的參數,IE9以上支持

    setInterval: 以固定的時間間隔重復調用一個函數或者代碼段

    var intervalId = window.setInterval(func, delay[, param1, param2, ...]);
    var intervalId = window.setInterval(code, delay);
    

    intervalId: 重復操作的IDfunc: 延遲調用的函數code: 代碼段delay: 延遲時間,沒有默認值

    setImmediate: 在瀏覽器完全結束當前運行的操作之后立即執行指定的函數(僅IE10和Node 0.10+中有實現),類似setTimeout(func, 0)

    var immediateId = setImmediate(func[, param1, param2, ...]);
    var immediateId = setImmediate(func);
    

    immediateId: 定時器IDfunc: 回調

    requestAnimationFrame: 專門為實現高性能的幀動畫而設計的API,但是不能指定延遲時間,而是根據瀏覽器的刷新頻率而定(幀)

    var requestId = window.requestAnimationFrame(func);

    func: 回調

    上面簡單的介紹了四種JS的定時器,而本文將會主要介紹比較常用的兩種:setTimeout和setInterval。

    二、舉個栗子

    • 基本用法
    // 下面代碼執行之后會輸出什么?
    var intervalId, timeoutId;
    
    timeoutId = setTimeout(function () {
        console.log(1);
    }, 300);
    
    setTimeout(function () {
        clearTimeout(timeoutId);
        console.log(2);
    }, 100);
    
    setTimeout('console.log("5")', 400);
    
    intervalId = setInterval(function () {
        console.log(4);
        clearInterval(intervalId);
    }, 200);
    
    // 分別輸出: 2、4、5
    
    • setInterval 和 setTimeout的區別?
    // 執行在面的代碼塊會輸出什么?
    setTimeout(function () {
        console.log('timeout');
    }, 1000);
    
    setInterval(function () {
        console.log('interval')
    }, 1000);
    
    // 輸出一次 timeout,每隔1S輸出一次 interval
    
    /*--------------------------------*/
    
    // 通過setTimeout模擬setInterval 和 setInterval有啥區別么?
    var callback = function () {
        if (times++ > max) {
            clearTimeout(timeoutId);
            clearInterval(intervalId);
        }
    
        console.log('start', Date.now() - start);
        for (var i = 0; i < 990000000; i++) {}
        console.log('end', Date.now() - start);
    },
    delay = 100,
    times = 0,
    max = 5,
    start = Date.now(),
    intervalId, timeoutId;
    
    function imitateInterval(fn, delay) {
        timeoutId = setTimeout(function () {
            fn();
    
            if (times <= max) {
                imitateInterval(fn ,delay);
            }
        }, delay);
    }
    
    imitateInterval(callback, delay);
    intervalId = setInterval(callback, delay);
    

    如果是setTimeout和setInterval的話,它倆僅僅在執行次數上有區別,setTimeout一次、setIntervaln次。 而通過setTimeout模擬的setInterval與setInterval的區別則在于:setTimeout只有在回調完成之后才會去調用下一次定時器,而setInterval則不管回調函數的執行情況,當到達規定時間就會在事件隊列中插入一個執行回調的事件,所以在選擇定時器的方式時需要考慮setInterval的這種特性是否會對你的業務代碼有什么影響?

    • setTimeout(func, 0) 和 setImmediate(func)誰更快?(僅僅是好奇,才寫的這段測試)
    console.time('immediate');
    console.time('timeout');
    
    setImmediate(() => {
        console.timeEnd('immediate');
    });
    
    setTimeout(() => {
        console.timeEnd('timeout');
    }, 0);
    

    在Node.JS v6.7.0中測試發現setTimeout更早執行

    • 面試題

    下面代碼運行后的結果是什么?

    // 題目一
    var t = true;
    
    setTimeout(function(){
        t = false;
    }, 1000);
    
    while(t){}
    
    alert('end');
    
    /*--------------------------------*/
    
    // 題目二
    for (var i = 0; i < 5; i++) {
        setTimeout(function () {
            console.log(i);
        }, 0);
    }
    
    /*--------------------------------*/
    
    // 題目三
    var obj = {
        msg: 'obj',
        shout: function () {
            alert(this.msg);
        },
        waitAndShout: function() {
            setTimeout(function () {
                this.shout();
            }, 0);    
        }
    };
    obj.waitAndShout();
    

    問題答案會在后面解答

    三、JS定時器的工作原理

    在解釋上面問題的答案之前我們先來了解一下定時器的工作原理,這里將用引用How JavaScript Timers Work中的例子來解釋定時器的工作原理,該圖為一個簡單版的原理圖。JS 定時器的4種寫法及介紹

    Timers

    上圖中,左側數字代表時間,單位毫秒;左側文字代表某一個操作完成后,瀏覽器去詢問當前隊列中存在哪些正在等待執行的操作;藍色方塊表示正在執行的代碼塊;右側文字代表在代碼運行過程中,出現哪些異步事件。該圖大致流程如下:

    • 程序開始時,有一個JS代碼塊開始執行,執行時長約為18ms,在執行過程中有3個異步事件觸發,其中包括一個setTimeout、鼠標點擊事件、setInterval
    • 第一個setTimeout先運行,延遲時間為10ms,稍后鼠標事件出現,瀏覽器在事件隊列中插入點擊的回調函數,稍后setInterval運行,10ms到達之后,setTimeout向事件隊列中插入setTimeout的回調
    • 當第一個代碼塊執行完成后,瀏覽器查看隊列中有哪些事件在等待,他取出排在隊列最前面的代碼來執行
    • 在瀏覽器處理鼠標點擊回調時,setInterval再次檢查到到達延遲時間,他將再次向事件隊列中插入一個interval的回調,以后每隔指定的延遲時間之后都會向隊列中插入一個回調
    • 后面瀏覽器將在執行完當前隊頭的代碼之后,將再次取出目前隊頭的事件來執行

    這里只是對定時器的原理做一個簡單版的描述,實際的處理過程比這個復雜。

    四、題目答案

    好啦,我們現在再來看看上面的面試題的答案。 第一題

    alert永遠都不會執行,因為JS是單線程的,且定時器的回調將在等待當前正在執行的任務完成后才執行,而while(t) {}直接就進入了死循環一直占用線程,不給回調函數執行機會

    第二題

    代碼會輸出 5 5 5 5 5,理由同上,當i = 0時,生成一個定時器,將回調插入到事件隊列中,等待當前隊列中無任務執行時立即執行,而此時for循環正在執行,所以回調被擱置。當for循環執行完成后,隊列中存在著5個回調函數,他們的都將執行console.log(i)的操作,因為當前JS代碼上中并沒有使用塊級作用域,所以i的值在for循環結束后一直為5,所以代碼將輸出5個5

    第三題

    這個問題涉及到this的指向問題,由setTimeout()調用的代碼運行在與所在函數完全分離的執行環境上. 這會導致這些代碼中包含的this關鍵字會指向window (或全局)對象,window對象中并不存在shout方法,所以就會報錯,修改方案如下:

    var obj = {
        msg: 'obj',
        shout: function () {
            alert(this.msg);
        },
        waitAndShout: function() {
            var self = this; // 這里將this賦給一個變量
            setTimeout(function () {
                self.shout();
            }, 0);    
        }
    };
    obj.waitAndShout();
    

    五、需要注意的點

    • setTimeout有最小時間間隔限制,HTML5標準為4ms,小于4ms按照4ms處理,但是每個瀏覽器實現的最小間隔都不同
    • 因為JS引擎只有一個線程,所以它將會強制異步事件排隊執行
    • 如果setInterval的回調執行時間長于指定的延遲,setInterval將無間隔的一個接一個執行
    • this的指向問題可以通過bind函數、定義變量、箭頭函數的方式來解決

    版權聲明:本文內容由互聯網用戶自發貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發現本站有涉嫌抄襲侵權/違法違規的內容, 請發送郵件至 舉報,一經查實,本站將立刻刪除。

    發表評論

    登錄后才能評論
    国产精品区一区二区免费