setTimeout和setInterval深入理解

一债鸡、setTimeout和setInterval定時器的參數(shù)

setTimeout和setInterval函數(shù)用來指定某個函數(shù)或某段代碼显拜,在多少毫秒之后插入異步隊列贫堰。它返回一個整數(shù),表示定時器的編號毯欣,以后可以用來取消這個定時器盲厌。
var timeoutID = scope.setTimeout(function[, delay, param1, param2])
定時器可以接受三個參數(shù)署照,delay表示執(zhí)行的代碼祸泪,delay表示延遲的時間(可選,省略默認為0)建芙,param1没隘、param2 回調(diào)函數(shù)接收的參數(shù)。

1岁钓、第一個參數(shù): 執(zhí)行的代碼

執(zhí)行的代碼一般為一個函數(shù)升略,使用時需要注意函數(shù)this指向問題。下面函數(shù)打印出來的name為undefined屡限,因為this指向window,
原因為setTimeout中沒有保存obj.sayName的執(zhí)行環(huán)境

var obj = {
    name: 'hc3001',
    sayName: function() {
        console.log(this.name)
    }
}
setTimeout(obj.sayName, 1000) //undefined

// 利用閉包直接調(diào)用obj.sayName()
var obj = {
    name: 'hc3001',
    sayName: function() {
        console.log(this.name)
    }
}
setTimeout(function() {
    obj.sayName()
}, 1000)  // 'hc3001'

//用bind解決
setTimeout(obj.sayName.bind(obj), 1000) 
// 'hc3001'
2炕倘、第二個參數(shù): 時間钧大,時間單位為毫秒,此參數(shù)比較具有迷惑性罩旋。

a啊央、當時間為0時,并不代表立即執(zhí)行涨醋,后面setTimeout 瓜饥、setInterval運行機制會詳細講述。下面用一個函數(shù)直觀看下浴骂,當時間為0時乓土,一分鐘內(nèi)執(zhí)行的次數(shù)(‘Javascript 異步編程’中提到)。

const timer = function() {
    var times = 1000 + Date.now()
    var count = 0
    var i = setInterval(function() {
        if(Date.now() > times) {
            clearInterval(i)
            console.log('count', count)
        }
        count++
    }, 0)
}
timer()

上述代碼條件是:同步任務的主線程執(zhí)行時間接近為0溯警,異步隊列中無其他程序執(zhí)行趣苏。
瀏覽器環(huán)境:count 252 也就是表示3.96毫秒執(zhí)行一次。
node環(huán)境:count 706 1.41毫秒執(zhí)行一次梯轻。
所以定時器設置0秒不管哪個環(huán)境0毫秒都是上達不到的食磕。根據(jù)[HTML5標準],setTimeOut推遲執(zhí)行的時間喳挑,最少是4毫秒彬伦。如果小于這個值,會被自動增加到4伊诵。這是為了防止多個setTimeout(f,0)語句連續(xù)執(zhí)行单绑,造成性能問題。

b日戈、當時間不為0時询张,表示在規(guī)定時間后將代碼插入到異步隊列中,而并不是在規(guī)定時間后執(zhí)行代碼浙炼。至于代碼具體執(zhí)行的時間要看異步隊列中有無其他程序份氧。利用時間為0時唯袄,我們可以實現(xiàn)函數(shù)的異步操作。

var fs = require('fs')
//異步執(zhí)行函數(shù)
const async = function(func) {
    setTimeout(function(
         func()
    ) , 0)
}
console.log('異步讀寫')
const sync = function() {
    var readData = fs.readFileSync('data1.txt', 'utf8')
    var writeData = fs.writeFileSync('data2.txt', readData)
    console.log('同步寫入完成')
}
async(sync)

上面代碼蜗帜,把本來是同步的readFileSync 和 writeFileSync 放入setTimeout中實現(xiàn)異步操作恋拷,不用等待,這樣可以解決異步回調(diào)地獄的問題厅缺。
c蔬顾、此參數(shù)有范圍限制[0, 2^31 - 1],如果超過這個范圍則會初始化為 0湘捎。

3诀豁、第三個參數(shù): param

param為回調(diào)函數(shù)接收的參數(shù),目前IE9不支持此參數(shù)

setTimeout(function(a,b){
  console.log(a+b);
},1000,1,1);
二窥妇、setTimeout舷胜、setInverval運行機制

1、定時器的運行機制是將指定的代碼在規(guī)定的時間內(nèi)插入下一輪異步隊列中活翩,并且在同步任務主線程代碼執(zhí)行完畢烹骨,異步隊列無其他程序的情況下才開始執(zhí)行。

setTimeout(function(){
    console.log(1)
});
console.log(0)
//其他代碼需要100毫秒

上述代碼中同步任務主線程代碼需要執(zhí)行100毫秒材泄,再執(zhí)行setTimeout中的代碼(已在0秒放入異步隊列)沮焕,此時的客觀上表現(xiàn)為100毫秒后執(zhí)行的setTimeout中代碼。這就是0秒不會立即執(zhí)行的原因之一(主線程執(zhí)行時間就算接近0毫秒拉宗,也不會立即執(zhí)行異步隊列中的代碼峦树,參數(shù)部分已經(jīng)詳述)。

2簿废、這樣的運行機制會在使用setInterval時產(chǎn)生的一些問題空入。以下面代碼和圖片為例(高級程序設計)

var btn = document.querySelector('.my-btn')
btn.addEventListener((e)=> {
    var t = setInterval(()=> {
        console.log('success')
    }, 200)
})
setInterval.jpg

事件處理時間(黃色300毫秒部分)、代碼插入起始時間255毫秒族檬、第一個插入代碼執(zhí)行起始時間300毫秒歪赢、代碼執(zhí)行總時間325毫秒(圖中未標識)。

產(chǎn)生的問題:某些間隔會被跳過单料、多個定時器的代碼執(zhí)行之間的間隔可能會比預期的小埋凯。
原因:
(1)、第1 個定時器是在205ms 處添加到隊列中的扫尖,但是直到過了300ms 處才能夠執(zhí)行白对。
(2)、在把指定的代碼移到下一輪異步隊列之前换怖,如果Event Loop還存在上一輪的代碼沒有執(zhí)行甩恼。這種情況下,js是不會代碼移入異步隊列的。
(3)条摸、如果是625毫秒執(zhí)行完一輪代碼悦污,第二輪代碼就會立即執(zhí)行,因為第二輪代碼已經(jīng)在405毫秒插入異步隊列钉蒲,這就在直觀上造成了間隔時間跳過切端,代碼是連續(xù)執(zhí)行的。
解決方案:

setTimeout(function() {
   //處理中100毫秒
   setTimeout(arguments.callee, interval)
}, interval)

這種setTimeout方案一定要處理100毫秒代碼才調(diào)用‘插入到異步隊列的setTimeout代碼’顷啼。

3踏枣、這樣的運行機制和循環(huán)結(jié)合,會產(chǎn)生不少疑惑的事情钙蒙,如下面常見的所謂‘閉包’面試題茵瀑。

for (var i = 0; i < 5; i++) {
   setTimeout(function() {
       console.log('i', i)
   }, 1000)
}
//立即輸出5個5

for (var i = 0; i < 5; i++) {
   (function (i) {
       setTimeout(function() {
           console.log('i', i)
       }, 1000)
   })(i)
}
//立即輸出0、1躬厌、2瘾婿、3、4

for (var i = 0; i < 5; i++) {
   (function (i) {
       setTimeout(function() {
           console.log('i', i)
       }, 1000*i)
   })(i)
}
//每隔一秒輸出0烤咧、1、2抢呆、3煮嫌、4
三倚舀、定時器回調(diào)在異步隊列中的順序

1惫叛、在異步任務中有不同的觸發(fā)形式把回調(diào)代碼插入異步隊列(Even Queue)中,比如:new Promise()言疗、setTimeout恳邀,他們插入代碼是有一定順序的懦冰。

setTimeout(function() {
    console.log('setTimeout')
})

new Promise(function(resolve) {
    console.log('promise')
    resolve()
}).then(function() {
    console.log('then')
})

console.log('console')
//輸出結(jié)果順序:Promise、console谣沸、 then刷钢、setTimeout

setTimeout(function() {
    console.log('setTimeout')
})
const readFilepromise = function() {
    const ajax = new Promise(function(resolve, rejected) {
        console.log('readFile')
        const fs = require('fs')
        const options = {
            encoding: 'utf8'
        }
        fs.readFile('test1.txt', options, function(error, content) {
            if (error !== null) {
                reject(error)
            } else {
                resolve(content)
            }
        })
    })
    return ajax
}
readFilepromise().then(
    function(d) {
        console.log('d', d)
    }
)
console.log('console')
//輸出結(jié)果順序:readFile、console乳附、 setTimeout内地、文件內(nèi)容

順序的關鍵是判斷then和setTimeout哪個先輸出。異步任務中完成指定的事所花時間是不定的赋除,在異步任務中setTimeout 雖然是0秒但還是要花一定時間把回調(diào)函數(shù)插入到異步隊列中阱缓,而promise.then此時是不需要時間插入異步隊列的(有的文章把setTimeout當宏任務、promise.then當微任務來理解举农。https://juejin.im/post/5b498d245188251b193d4059)荆针。但是當promise.then是讀取文件時,這個讀取文件的時間就不定了(依據(jù)文件大小來),導致promise.then后插入異步隊列中航背。這部分涉及到JavaScript執(zhí)行機制可以參考下圖喉悴,具體細節(jié)可以看下軟一峰老師的‘JavaScript 運行機制詳解:再談Event Loop’。

JavaScript執(zhí)行機制.png

四沃粗、clearTimeout() 和 clearInterval()

setTimeout和setInterval函數(shù)粥惧,都會返回一個表示計數(shù)器編號的隨機整數(shù)值,將該整數(shù)傳入clearTimeout和clearInterval函數(shù)最盅,就可以取消對應的定時器突雪。

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

clearTimeout(id1)
clearInterval(id2)  

1、利用clearTimeout() 我們可以做一個簡單的倒計時功能涡贱。

    <div>
        倒計時:
        <span class="timing"></span>
    </div>
    <script>
        var accountTime = 3
        document.querySelector('.timing').innerHTML = accountTime
        var timing = function() {
            var t = setTimeout((function() {
                if(accountTime > 0) {
                    accountTime--
                    document.querySelector('.timing').innerHTML = accountTime
                    setTimeout(timing, 1000)
                } else {
                   accountTime = 0
                    document.querySelector('.timing').innerHTML = accountTime
                    clearTimeout(t)
                }
            }), 1000)
        }
        timing()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咏删,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子问词,更是在濱河造成了極大的恐慌督函,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件激挪,死亡現(xiàn)場離奇詭異辰狡,居然都是意外死亡,警方通過查閱死者的電腦和手機垄分,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門宛篇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人薄湿,你說我怎么就攤上這事叫倍。” “怎么了豺瘤?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵吆倦,是天一觀的道長。 經(jīng)常有香客問我坐求,道長蚕泽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任瞻赶,我火速辦了婚禮赛糟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘砸逊。我一直安慰自己璧南,他們只是感情好,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布师逸。 她就那樣靜靜地躺著司倚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上动知,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天皿伺,我揣著相機與錄音,去河邊找鬼盒粮。 笑死鸵鸥,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的丹皱。 我是一名探鬼主播妒穴,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼摊崭!你這毒婦竟也來了讼油?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤呢簸,失蹤者是張志新(化名)和其女友劉穎矮台,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體根时,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡瘦赫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蛤迎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耸彪。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖忘苛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唱较,我是刑警寧澤扎唾,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站南缓,受9級特大地震影響胸遇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜汉形,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一纸镊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧概疆,春花似錦逗威、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春罐呼,著一層夾襖步出監(jiān)牢的瞬間鞠柄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工嫉柴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留厌杜,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓计螺,卻偏偏與公主長得像夯尽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子危尿,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容