前端進階之setTimeout 倒計時爲何會出現偏差?

  • 做者:陳大魚頭
  • github: KRISACHAN
  • 連接:github.com/YvetteLau/S…
  • 背景:最近高級前端工程師 劉小夕github 上開了個每一個工做日佈一個前端相關題的 repo,懷着學習的心態我也參與其中,如下爲個人回答,若是有不對的地方,很是歡迎各位指出。

線程與進程

相信你們常常會聽到一句話,就是 「JS是單線程的」,但是什麼是 線程,什麼又是 單線程,有 多線程 嗎?javascript

定義

講到線程,那麼確定也得說一下進程。其實在本質上,兩個名詞都是 CPU 工做時間片的一個描述。前端

進程(process) 指的是CPU 在 運行指令及加載和保存上下文所需的時間,放在應用上是指計算機中已運行的程序。java

線程(thread) 是操做系統可以進行運算的最小單位。它被包含在 進程 之中,描述了執行一段指令所需的時間。node

  • 單線程:按代碼書寫順序從頭至尾,一行一行地執行代碼,若是其中一行代碼報錯,那麼剩下代碼將再也不執行。容易阻塞代碼。
  • 多線程:代碼運行的環境不一樣,各線程獨立,互不影響,避免阻塞。

瀏覽器中的線程

瀏覽器中的線程分了如下幾類:git

  • JS線程
  • UI線程
  • event線程
  • 定時器線程
  • http線程

執行棧

執行棧能夠理解爲是用來存儲函數調用的棧,遵循先進後出的原則。github

事件循環

node端

Node 的 Event Loop 分爲 6 個階段,它們會按照順序反覆運行。每當進入某一個階段的時候,都會從對應的回調隊列中取出函數去執行。當隊列爲空或者執行的回調函數數量到達系統設定的閾值,就會進入下一階段。promise

Event Loop 6 個階段:瀏覽器

  1. timers
  2. I/O callbacks
  3. idle, prepare
  4. poll
  5. check
  6. close callbacks

瀏覽器端

瀏覽器端 的狀況與 node端 的狀況相仿,當咱們執行 JS 代碼的時候其實就是往執行棧中放入函數,當遇到異步的代碼時,會被掛起並在須要執行的時候加入到 Task(有多種 Task) 隊列中。一旦執行棧爲空,Event Loop 就會從 Task 隊列中拿出須要執行的代碼並放入執行棧中執行。微信

  • 微任務(microtask)
    • process.nextTick
    • promise
    • Object.observe(曾經是提案,現在已經廢除)
    • MutationOberver
  • 宏任務(macrotask)
    • script
    • setTimeout
    • setInterval
    • setImmediate
    • I/O
    • UI渲染

執行順序以下:前端工程師

  1. 執行同步代碼,這是宏任務
  2. 執行棧爲空,查詢是否有微任務要執行
  3. 必要時渲染UI
  4. 進行下一輪的 EventLoop ,執行宏任務中的異步代碼

setTimeout 偏差

上面講了定時器是屬於 宏任務(macrotask) 。若是當前 執行棧 所花費的時間大於 定時器 時間,那麼定時器的回調在 宏任務(macrotask) 裏,來不及去調用,全部這個時間會有偏差。

咱們看如下代碼:

setTimeout(function () {
	console.log('biubiu');
}, 1000);

某個執行時間很長的函數();
複製代碼

若是定時器下面的函數執行要 5秒鐘,那麼定時器裏的log 則須要 5秒以後再執行,函數佔用了當前 執行棧 ,要等執行棧執行完畢後再去讀取 微任務(microtask),等 微任務(microtask) 完成,這個時候纔會去讀取 宏任務(macrotask) 裏面的 setTimeout 回調函數執行。setInterval 同理,例如每3秒放入宏任務,也要等到執行棧的完成。

還有一種狀況以下:

setTimeout(function() {
    setTimeout(function() {
        setTimeout(function() {
            setTimeout(function() {
                setTimeout(function() {
                    setTimeout(function() {
                        console.log('嚶嚶嚶');
                    }, 0);
                }, 0);
            }, 0);
        }, 0);
    }, 0);
}, 0);
複製代碼

在最新的規範裏有這麼一句: If nesting level is greater than 5, and timeout is less than 4, then increase timeout to 4.

因此意思就是意思就是若是timeout嵌套大於 5層,而時間間隔小於4ms,則時間間隔增長到4ms。



若是你、喜歡探討技術,或者對本文有任何的意見或建議,你能夠掃描下方二維碼,關注微信公衆號「 魚頭的Web海洋」,隨時與魚頭互動。歡迎!衷心但願能夠碰見你。

相關文章
相關標籤/搜索