用故事解析setTimeout和setInterval(内含js單線程和任務隊列)
差別:
setTimeout(fn,t):
延遲調用,超過了時間就調用回調函數,傳回一個id,使用clearTimeout(id)取消執行。
注意:
取消了裡面的回調函數就不執行了哦,而不是取消的時候就立即執行,下面有源碼可以自己cv試一下。setInterval(fn,t):
循環調用,有周期性的調用回調函數,傳回一個id,使用clearInterval(id)取消執行。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鳥教程(runoob.com)</title>
<script>
console.log('333333333333333333333')
var aa =setTimeout(()=>{
console.log("11111111111111")
},6000)
setTimeout(()=>{
clearTimeout(aa)
console.log("2222222222")
},3000)
</script>
</head>
<body>
</body>
</html>
在學習這兩種延遲函數之前要最好是先了解一下JS單線程和任務隊列;
舉個栗子:
在廣場上,隻有一家一點點奶茶店(
cpu),然後我(
一個任務)要去買奶茶(
執行這個任務),隻能排一條隊(
js的單線程機制),雖說排隊(
主線程)的人很多,但是一點點從業人員工作速度确實很快,一般的人付完現金就完了(
synchronous同步任務)。
但是到了我的時候,我想用支付寶支付,但是打開支付寶需要網絡,需要等待支付寶軟體打開(
asynchronous異步任務,等待IO裝置傳回結果),但是為了不影響隊列後面的排隊者的速度,是以一點點的從業人員讓我去旁邊的隊列裡(
任務隊列)去等待,這個隊列裡面有加我一起有三個人在啟動支付寶,我排在第三。
然後一段時間過後,我的和第一個人支付寶打開了(
IO裝置傳回結果了),第二個人沒打開,我和第一個人就把二維碼展示出來(
在任務隊列中添加一個事件,表示相關異步任務可以進入主線程執行了),但是這個時候主隊列裡面還有一些人在買奶茶,是以,現在從業人員顧不上我。
然後我們就隻能一直等待,直到主隊列裡面買奶茶的人都走光了,這個時候從業人員就把我和第一個人加入主隊列中,然後我和第一個人就相繼都買到奶茶了,主隊列又沒有人了,于是從業人員又去問第二個人二維碼有沒有出來,(
這個過程叫Event Loop(事件循環)),至此基本的單線程和任務隊列就完了,下面再了解一下定時器的特殊原理。
setTimeout(fn,t)
同樣的,又是我(
setTimeout(fn,20)函數)去買奶茶,我首先還是去排隊,到我的時候我說我現在不想買,我要等到20(
設定的時長)毫秒之後再買,從業人員就說,好,那你去任務隊列裡面去等20毫秒吧,然後我就屁颠屁颠的去了。
20毫秒後,還是老樣子,從業人員要等到主隊列裡面人都走完了再把我拉到主隊列裡面去,是以我實際等待的時間=我設定的時間+設定時間到了之後主隊列裡面執行的時間,但是慶幸的是從業人員工作速度非常快,是以一般主隊列裡面是沒有人的。
注意:
1、是以即使你設定逾時時間為0,從業人員也不會立馬執行你哦,還是會把你加到任務隊列中的,誰叫你是asynchronous(異步任務)
2、你設定逾時時間有用嗎?實際上是沒有生效的,因為html5标準規定了setTimeout設定逾時時間最小值是4毫秒,如果低于這個值就會自動增加,老版本浏覽器更将是把最短時間設為10毫秒,而最要命的是,如果涉及到DOM的變動的都是16毫秒一次。
setInterval(fu,t)
同樣的,鳴人(
setInterval(fn,1000)函數)去買奶茶,首先還是去排隊,到鳴人的時候,它不僅要在1秒鐘之後買,還要影分身出很多鳴人相繼在1、2、3、4...之後買,從業人員同樣要他和他的影分身在任務隊列後面排隊等會,正常情況下,主隊列沒人了,鳴人和他的影分身們也會在規定的時間裡面相繼執行。
那麼問題來了
假如每個鳴人都很喜歡逼逼叨叨,那麼就會發生這樣子的情況:
當一秒鐘後第一個鳴人從任務隊列中到主隊列中的時候,就一直跟從業人員逼逼叨叨(
發生阻塞了,例如遇到循環不完的for),直到第四秒鐘才說完(也就是b了3秒鐘),這個時候第二個鳴人已經超過預定時間的兩秒鐘了,然後等到第二個鳴人和從業人員聊得時候又逼逼叨叨不停,又要b3秒鐘,是以第二個鳴人會在第7秒b完,同理,第三個和第四個分别在第10秒和第13秒b完。
鳴人逼逼叨叨的過程如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>uaoie.top</title>
<script>
console.log('333333333333333333333')
var y=0;
var x = new Date().getTime();
var d=setInterval(a,1000);
function a() {
y++;
sleep(3000);
if(y>=4){
clearInterval(d)
}
console.log(new Date().getTime()-x);
}
function sleep(sleepTime){
var start=new Date().getTime();
while(true){
if(new Date().getTime()-start>sleepTime){
break;
}
}
}
</script>
</head>
<body>
</body>
</html>
導緻的情況如下:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLzkjM4ADM1YjNx0SOyEzM1gjM1EzMwkDM5EDMy0CO0ADM3MTMvwVOwkTMwIzLchDNwAzNzEzLcd2bsJ2Lc12bj5ycn9Gbi52YugTMwIzZtl2Lc9CX6MHc0RHaiojIsJye.png)
全劇終
八、彩蛋
現在我們可以很輕松的分析一下下面代碼的原理(雖然我們都知道結果):
var aa=new Date().getTime()
for (var i = 0; i < 4; i++) {
setTimeout(function() {
console.log(new Date().getTime() -aa, i);
}, 1000);
}
console.log(new Date().getTime() -aa, i);
先執行第一行,完了後執行for,循環4次也就是執行4次setTimeout函數,這個4個函數同時放在任務隊列中。
然後再執行最下面的config,是以會先列印0秒和4。
然後等到一秒後,setTimeout函數從任務隊列中出來到主隊列,輸出的是1秒和4。
是以效果如下:
posted on 2019-09-03 14:51 買辣椒也用券 閱讀( ...) 評論( ...) 編輯 收藏
轉載于:https://www.cnblogs.com/Juaoie/p/11446302.html