從setTimeout與setInterval看JS運行機制

從setTimeout與setInterval看JS運行機制

一:平時簡單使用

1.setTimeout延遲一段時間執行一次(only one)

setTimeout(function, milliseconds, param1, param2, ...)clearTimeout() // 阻止定時器運行setTimeout(function(){ alert("Hello"); }, 3000); // 3s後彈出

2.setInterval 每隔一段時間執行一次 (Many times)

setInterval(function, milliseconds, param1, param2, ...)e.g. setInterval(function(){ alert("Hello"); }, 3000); // 每隔3s彈出

3.setTimeout和setInterval的延時最小間隔是4ms;在JavaScript中沒有任何代碼是立刻執行的,但一旦進程空閒就儘快執行。所以 setTimeout 還是setInterval,設置的時間都是n毫秒被添加到隊列中,而不是過n毫秒後立即執行。

二:進程與線程

1.模擬一個場景
  • 有一個大型工廠

  • 工廠有若干車間,每次只能有一個車間作業

  • 每個車間 有若干工人在流水線作業

那麼:

  • 一個工廠對應的就是計算機的一個cpu,平時講的多核就代表多個工廠

  • 每個工廠裡的車間就是進程,意味著同一時刻一個cpu只運行一個進程,其餘進程怠工

  • 這個運行的車間(進程)裡的工人,就是線程,可以有多個工人(線程)

  • 車間(進程)裡的房間,代表內存

在深入一點:

  • 車間(進程)裡工人可以隨意在多個房間(內存)之間走動,意味著一個進程裡,多個線程可以共享內存

  • 部分房間(內存)有限,只允許一個工人(線程)使用,此時其他工人(線程)要等待

  • 房間裡有工人進去後上鎖,其他工人需要等房間(內存)裡的工人(線程)開鎖出來後,才能進去,這就是互斥鎖

再深入:

  • 如果同時有多個車間作業,就是多進程

  • 如果一個車間裡有多個工人協同作業,就是多線程

  • 當然不同車間之間的工人也可以有互相協作,就需要協調機制

三:JavaScript單線程

JavaScript這門語言的核心特徵,就是單線程(是指在JS引擎中負責解釋和執行JavaScript代碼的線程只有一個)。這和JavaScript最初設計是作為一門GUI變成語言有關,最初用於瀏覽器端,單一線程控制GUI是很普遍的做法。雖然JavaScript是單線程,當瀏覽器是多線程的!!!!!!!例如webkit或是Gecko引擎,可能有JavaScript引擎線程,界面渲染線程,瀏覽器事件觸發線程,Http請求線程,讀寫文件的線程。

四:同步異步

1.同步(synchronous):假如一個函數返回時,調用者就能夠得到預期結果(即拿到了預期的返回值或者看到了預期的效果)這就是同步函數

e.g.alert('馬上能看到我拉');console.log('也能馬上看到我哦');

2.異步(asynchronous):假如一個函數返回時,調用者不能得到預期結果,需要通過一定手段才能獲得,這就是異步函數。

e.g.setTimeout(function() { // 過一段時間才能執行我哦}, 1000);

五:異步構成要素

一個異步過程通常是這樣的:主線程發起一個異步請求,相應的工作線程(比如瀏覽器的其他線程)接受請求並告知主線程已收到(異步函數返回);主線程可以繼續執行 後面代碼,同時工作線程執行異步任務,工作線程完成工作號,告知主線程;主線程收到通知後,執行一定的動作(調用回調函數)

六:通信機制

異步過程的通信機制:工作線程將消息放到消息隊列,主線程通過事件循環過程去取消息

七:事件循環Event Loop

主線程(js線程)只會做一件事,就是從消息隊列裡面取消息,執行消息,再取消息,再執行。消息隊列為空時,就會等待直到消息隊列變成非空。只有當前的消息執行結束,才會去取下一個消息。這種機制就叫做事件循環機制Event Loop,取一個消息並執行的過程叫做一次循環

從setTimeout與setInterval看JS運行機制

工作線程是生產者,主線程是消費者。工作線程執行異步任務,執行完成後把對應額回調函數封裝成一條消息放到消息隊列中;主線程不斷地從消息隊列中取消息並執行。當消息隊列為空時,主線程阻塞,直到消息隊列再次非空。

八:setTimeOut(function,0)發生了什麼

setTimeOut的function的任務異步執行,0不代表立即執行,而是將任務推到消息隊列的最後,再由主線程的事件循環調用。

setTimeOut的最小時間不是0ms而是4ms

九:setInterval缺點

定時器指定的時間間隔,表示的是何時將定時器的代碼添加到消息隊列,而不是何時執行代碼,所以真正何時執行代碼的時間是不能保證的。取決於何時被主線程的事件循環取到,並執行。

setInterval(function, N)

上面代碼意味著,每隔N秒把function事件推到消息隊列中,什麼時候執行,就不知道了

從setTimeout與setInterval看JS運行機制

上圖可見,setInterval每隔100ms往隊列中添加一個事件;100ms後,添加T1定時器代碼至隊列中,主線程中還有任務在執行,所以等待,some event執行結束後執行T1定時器代碼;又過了100ms,T2定時器被添加到隊列中,主線程還在執行T1代碼,所以等待;又過了100ms,理論上又要往隊列裡推一個定時器代碼,但由於此時T2還在隊列中,所以T3不會被添加,結果就是此時被跳過;這裡我們可以看到,T1定時器執行結束後馬上執行了T2代碼,所以並沒有達到定時器的效果。

setInterval兩個缺點:

  • 使用setInterval時,某些間隔會被跳過

  • 可能多個定時器會連續執行

十:鏈式setTimeOut

setTimeout(function () { // 任務 setTimeout(arguments.callee, interval);}, interval)

上述函數每次執行的時候都會創建一個新的定時器,第二個setTimeOut使用了arguments.callee()獲取當前函數的引用,並且為其設置另一個定時器。在一個定時器執行完前,不會向隊列插入新的定時器(解決缺點一),保證定時器間隔(解決缺點二)


分享到:


相關文章: