iT邦幫忙

2022 iThome 鐵人賽

DAY 29
0
Modern Web

前端蛇行撞牆記系列 第 29

Day29 前端蛇行撞牆記 - 可不可以給我一篇文章的時間? setTimeout() setInterval()

  • 分享至 

  • xImage
  •  

前言

可不可以給我一篇文章的時間? 來,setTimeout

時間從何而來?有看過星際效應嗎?在不同的星球時間也會不一樣,在 javascript 亦是如此....

不是拉!

這次會想寫是因為一直以為自己有理解 setTimeout() 搭配作用域的萬年題目,結果卻發現居然寫不出來,然後 setInterval() 也沒有實際用過,所以就來研究一下,順便重新理解 event loop。

setTimeout() 、 setInterval()

setTimeout()setInterval() 的全名應該是: window.setTimeout()window.setInterval() ,他們是一種 webAPI,是瀏覽器提供的函式可以讓我們去使用。

先來介紹一下這兩個的差異:

  • setTimeout():定時器,設定多久時間之後呼叫callback function,然後就結束了。
    • (just like 定時10分鐘之後吃藥,鬧鐘響了之後就結束。)
  • setInterval():週期性定時器,設定多久時間呼叫一次callback function,沒有設定終止條件的話會一直週期性呼叫。
    • (just like 定時每兩週要換隱形眼鏡,每兩週就會響一次。)
  • 會以毫秒來計算。 (1s 就是 1000)
  • 是一個瀏覽器提供的方法。
  • 兩者都會回傳一個 timeID 以用來取消時的函式傳入參數來完成取消。
    • 取消的相對應 webAPI 可以使用,clearTimeout()clearInterval()

setTimeout()

var timeoutID = scope.setTimeout(function[, delay]);

setTimeout 會回傳一個 正整數 ,這個整數是為了結束之後可以清理 stack 不會佔空間,或者有可能影響到其他的所以提供清除的方法。

會讓你傳的callback function 在 delay 的時間過後執行,

這裡最特別的事情是 setTimeout 是非同步的,然而 javascript 是單一執行緒,也就是說會一行一行執行下來,當遇到 setTimeout 的時候是會被告知說現在等 1 秒再執行裡面的 function,這個時候 javascript 為了讓畫面可以繼續執行,不會被這件事 blocking(堵塞)住,會把 setTimeout 放到 webAPI 等待,等到單一執行序的 stack 都結束了,就再把 setTimeout 裡的 callback function 放到 task quene ,1 秒之後執行。


來自 所以說event loop到底是什麼玩意兒?| Philip Roberts | JSConf EU 影片截圖

這就是 event loop ,更詳細可以去看這支影片:所以說event loop到底是什麼玩意兒?| Philip Roberts | JSConf EU

實際來看看差別:

console.log('start')

setTimeout(() => {
    console.log('Its setTimeout')
}, 1000)

console.log('end')

// start
// end
(一秒之後)
// Its setTimeout

javascript 會把現在可以執行的都執行完,等到裡面沒東西了,才會去執行 setTimeout 的 callback。

萬年題目

在講這個題目之前,應該要釐清一些問題:

  • 真正在寫程式碼的時候沒有人會讓 for...loop 是一個暴露的狀態,正常都會用 function 包起來,況且現在也沒有人會用 var 宣告了。
  • 這個題目跟var, let scope 作用域的關係比較大,setTimeout 本來就是最後執行的。

這個題目本來就不是正常的樣貌,就像那些 this 的題目,真的會有人在物件裏面那麼多層然後用 this 嗎? 雖然不是正常的樣貌,但還是要學習去拆解它,找到正確的做法。

解題時間

setTimeout() 裡面做一個 for...loop,希望可以印出 1, 2, 3, 4, 5。

for (var i = 0; i < 5; i++) {
    setTimeout(() => {
        console.log(i)
    },1000)
}

// 5, 5, 5, 5, 5

大家應該都知道這個狀況只要改成 let 就好了,這是因為 let 會讓變數鎖在作用域裡面,也就是 {} ,但是 var 只能鎖在 function 作用域裡面,但現在沒有在 function 裡面嘛,所以 for...loop 就很快速地跑完了,然後 setTimeout 剛不是說會放在 task quene 等待 stack 都結束才會執行嗎? 所以等到 1 秒之後, i 早就變成 5 了。

啊啊,我知道,就是用一個 IIFE 包起來就好了嘛,但要怎麼包?給你五秒想一下。

1
2
3
4
5

曾經我也以為我會,但是真的要寫出來的時候發現腦袋空空,IIFE也不熟,不知道該怎麼放? 那就直接用一個 function 包起來,再呼叫他吧!

for (var i = 0; i < 5; i++) {
    function count(i) {
            setTimeout(() => {
            console.log(i)
        },1000)
    }
    count(i)
}

// 0, 1, 2, 3, 4

解釋一下過程:

  1. for...loop 跑第一個 i = 0 的時候,進入 count(i) 裡面,此時 i = 0 的值已經存入 function count(i) 作用域裡面。
  2. 作用域裡面會收到 5 個的 i,因為傳了 5 次 i 進去 function count(i) 裡面,也都被儲存起來,當 stack 都結束之後,task quene 就會送出這五個 console.log(i) 出去。

用 IIFE 來做:

for (var i = 0; i < 5; i++) {
    (function(i) {
            setTimeout(() => {
            console.log(i)
        },1000)
    })(i)
}

// 0, 1, 2, 3, 4

經過了剛剛直接寫一個 function 之後應該有比較好理解 IIFE 為什麼要這麼寫了吧!

雖然知道要用 let 就可以解決這個問題,但還是要理解作用域的關係去解決問題。

不要死背啊,像當初我一樣

setInterval()

var intervalID = setInterval(function[, delay]);

使用方式跟 setTimeout 一模一樣,差別就是 setInterval 會在給的時間間隔不斷執行 callback function,我覺得 setInterval 可能比較適合使用清除的方法,因為如果 callback 沒有寫判斷式的話真的會執行到天荒地老啊。

立即來試試看:

const intervalId = setInterval(() => {
    console.log('setInterval')
}, 1000)

clearInterval(intervalId)

會在一秒之後印出 'setInterval',得趕緊拿到 intervalId ,然後執行 clearInterval(intervalId) 才能停止。

做一個倒數計時

setTimeInterval() 很適合做一個倒數計時啊!

<div>
	<p>倒數計時 90 秒</p>
	<p>還剩下:</p>
	<span></span>
	<span>秒</span>
</div>
const span = document.querySelector('span')

let time = 91;
setInterval(() => {
	if(time > 0) {
		time = time - 1;		
	} else {
		time = 'time out'
		return;
	}
	span.textContent = time;
}, 1000) ;

codepen: https://codepen.io/Jadddxx/pen/dyeaojp?editors=1010


參考資料:

JavaScript 中的同步與非同步(上):先成為 callback 大師吧!

所以說event loop到底是什麼玩意兒?| Philip Roberts | JSConf EU
談談 JavaScript 的 setTimeout 與 setInterval
JS设置定时器和清除定时器
MDN window.setTimeout
MDN setInterval()
w3c Window setInterval()
w3c Window setTimeout()


上一篇
Day28 前端蛇行撞牆記 - input 原生及 v-model 比較 / Vue 3 (下)
下一篇
Day30 前端蛇行撞牆記 - 30天內發生了什麼事?
系列文
前端蛇行撞牆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言