摘要:提出標準,允許腳本創建多個線程,但是子線程完全受主線程控制,且不得操作。所以,這個新標準并沒有改變單線程的本質。事件循環主線程線程只會做一件事,就是從消息隊列里面取消息執行消息,再取消息再執行。工作線程是生產者,主線程是消費者。
最近項目中遇到了一個場景,其實很常見,就是定時獲取接口刷新數據。那么問題來了,假設我設置的定時時間為1s,而數據接口返回大于1s,應該用同步阻塞還是異步?我們先整理下js中定時器的相關知識,再來看這個問題。初識setTimeout 與 setInterval
先來簡單認識,后面我們試試用setTimeout 實現 setInterval 的功能
setTimeout 延遲一段時間執行一次 (Only one)
setTimeout(function, milliseconds, param1, param2, ...) clearTimeout() // 阻止定時器運行 e.g. setTimeout(function(){ alert("Hello"); }, 3000); // 3s后彈出
setInterval 每隔一段時間執行一次 (Many times)
setInterval(function, milliseconds, param1, param2, ...) e.g. setInterval(function(){ alert("Hello"); }, 3000); // 每隔3s彈出
setTimeout和setInterval的延時最小間隔是4ms(W3C在HTML標準中規定);在JavaScript中沒有任何代碼是立刻執行的,但一旦進程空閑就盡快執行。這意味著無論是setTimeout還是setInterval,所設置的時間都只是n毫秒被添加到隊列中,而不是過n毫秒后立即執行。進程與線程,傻傻分不清楚
為了講清楚這兩個抽象的概念,我們借用阮大大借用的比喻,先來模擬一個場景:
這里有一個大型工廠
工廠里有若干車間,每次只能有一個車間在作業
每個車間里有若干房間,有若干工人在流水線作業
那么:
一個工廠對應的就是計算機的一個CPU,平時講的多核就代表多個工廠
每個工廠里的車間,就是進程,意味著同一時刻一個CPU只運行一個進程,其余進程在怠工
這個運行的車間(進程)里的工人,就是線程,可以有多個工人(線程)協同完成一個任務
車間(進程)里的房間,代表內存。
再深入點:
車間(進程)里工人可以隨意在多個房間(內存)之間走動,意味著一個進程里,多個線程可以共享內存
部分房間(內存)有限,只允許一個工人(線程)使用,此時其他工人(線程)要等待
房間里有工人進去后上鎖,其他工人需要等房間(內存)里的工人(線程)開鎖出來后,才能才進去,這就是互斥鎖(Mutual exclusion,縮寫 Mutex)
有些房間只能容納部分的人,意味著部分內存只能給有限的線程
再再深入:
如果同時有多個車間作業,就是多進程
如果一個車間里有多個工人協同作業,就是多線程
當然不同車間之間的工人也可以有相互協作,就需要協調機制
JavaScript 單線程總所周知,JavaScript 這門語言的核心特征,就是單線程(是指在JS引擎中負責解釋和執行JavaScript代碼的線程只有一個)。這和 JavaScript 最初設計是作為一門 GUI 編程語言有關,最初用于瀏覽器端,單一線程控制 GUI 是很普遍的做法。但這里特別要劃個重點,雖然JavaScript是單線程,但瀏覽器是多線程的?。?!例如Webkit或是Gecko引擎,可能有javascript引擎線程、界面渲染線程、瀏覽器事件觸發線程、Http請求線程,讀寫文件的線程(例如在Node.js中)。ps:可能要總結一篇瀏覽器渲染的文章了。
HTML5提出Web Worker標準,允許JavaScript腳本創建多個線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個新標準并沒有改變JavaScript單線程的本質。同步與異步,傻傻分不清楚
之前阮大大寫了一篇《JavaScript 運行機制詳解:再談Event Loop》,然后被樸靈評注了,特別是同步異步的理解上,兩位大牛有很大的歧義。
同步(synchronous):假如一個函數返回時,調用者就能夠得到預期結果(即拿到了預期的返回值或者看到了預期的效果),這就是同步函數。
e.g. alert("馬上能看到我拉"); console.log("也能馬上看到我哦");
異步(asynchronous):假如一個函數返回時,調用者不能得到預期結果,需要通過一定手段才能獲得,這就是異步函數。
e.g. setTimeout(function() { // 過一段時間才能執行我哦 }, 1000);異步構成要素
一個異步過程通常是這樣的:主線程發起一個異步請求,相應的工作線程(比如瀏覽器的其他線程)接收請求并告知主線程已收到(異步函數返回);主線程可以繼續執行后面的代碼,同時工作線程執行異步任務;工作線程完成工作后,通知主線程;主線程收到通知后,執行一定的動作(調用回調函數)。
發起(注冊)函數 -- 發起異步過程
回調函數 -- 處理結果
e.g. setTimeout(fn, 1000); // setTimeout就是異步過程的發起函數,fn是回調函數通信機制
異步過程的通信機制:工作線程將消息放到消息隊列,主線程通過事件循環過程去取消息。消息隊列 Message Queue
一個先進先出的隊列,存放各類消息。事件循環 Event Loop
主線程(js線程)只會做一件事,就是從消息隊列里面取消息、執行消息,再取消息、再執行。消息隊列為空時,就會等待直到消息隊列變成非空。只有當前的消息執行結束,才會去取下一個消息。這種機制就叫做事件循環機制Event Loop,取一個消息并執行的過程叫做一次循環。
工作線程是生產者,主線程是消費者。工作線程執行異步任務,執行完成后把對應的回調函數封裝成一條消息放到消息隊列中;主線程不斷地從消息隊列中取消息并執行,當消息隊列空時主線程阻塞,直到消息隊列再次非空。setTimeout(function, 0) 發生了什么
其實到這兒,應該能很好解釋setTimeout(function, 0) 這個常用的“奇技淫巧”了。很簡單,就是為了將function里的任務異步執行,0不代表立即執行,而是將任務推到消息隊列的最后,再由主線程的事件循環去調用它執行。
HTML5 中規定setTimeout 的最小時間不是0ms,而是4ms。setInterval 缺點
再次強調,定時器指定的時間間隔,表示的是何時將定時器的代碼添加到消息隊列,而不是何時執行代碼。所以真正何時執行代碼的時間是不能保證的,取決于何時被主線程的事件循環取到,并執行。
setInterval(function, N)
那么顯而易見,上面這段代碼意味著,每隔N秒把function事件推到消息隊列中,什么時候執行?母雞啊!
上圖可見,setInterval每隔100ms往隊列中添加一個事件;100ms后,添加T1定時器代碼至隊列中,主線程中還有任務在執行,所以等待,some event執行結束后執行T1定時器代碼;又過了100ms,T2定時器被添加到隊列中,主線程還在執行T1代碼,所以等待;又過了100ms,理論上又要往隊列里推一個定時器代碼,但由于此時T2還在隊列中,所以T3不會被添加,結果就是此時被跳過;這里我們可以看到,T1定時器執行結束后馬上執行了T2代碼,所以并沒有達到定時器的效果。
綜上所述,setInterval有兩個缺點:
使用setInterval時,某些間隔會被跳過;
可能多個定時器會連續執行;
鏈式setTimeoutsetTimeout(function () { // 任務 setTimeout(arguments.callee, interval); }, interval)
警告:在嚴格模式下,第5版 ECMAScript (ES5) 禁止使用 arguments.callee()。當一個函數必須調用自身的時候, 避免使用 arguments.callee(), 通過要么給函數表達式一個名字,要么使用一個函數聲明.
上述函數每次執行的時候都會創建一個新的定時器,第二個setTimeout使用了arguments.callee()獲取當前函數的引用,并且為其設置另一個定時器。好處:
在前一個定時器執行完前,不會向隊列插入新的定時器(解決缺點一)
保證定時器間隔(解決缺點二)
So...回顧最開始的業務場景的問題,用同步阻塞還是異步,答案已經出來了...
PS:其實還有macrotask與microtask等知識點沒有提到,總結了那么多,其實JavaScript深入下去還有很多,任重而道遠呀。
參考:
進程與線程的一個簡單解釋 -- 阮大大
【譯】JavaScript 如何工作的: 事件循環和異步編程的崛起 + 5 個關于如何使用 async/await 編寫更好的技巧
已同步至個人博客-軟硬皆施
Github 歡迎star :)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93410.html
摘要:刨根問底,這里說的成本,到底高在哪兒呢什么是文檔對象模型什么是可能很多人第一反應就是等標簽至少我是,但要知道,是,是,對象模型,是為提供的。操作具體的成本,說到底是造成瀏覽器回流和重繪,從而消耗資源。 從我接觸前端到現在,一直聽到的一句話:操作DOM的成本很高,不要輕易去操作DOM。尤其是React、vue等MV*框架的出現,數據驅動視圖的模式越發深入人心,jQuery時代提供的強大便...
摘要:刨根問底,這里說的成本,到底高在哪兒呢什么是文檔對象模型什么是可能很多人第一反應就是等標簽至少我是,但要知道,是,是,對象模型,是為提供的。操作具體的成本,說到底是造成瀏覽器回流和重繪,從而消耗資源。 從我接觸前端到現在,一直聽到的一句話:操作DOM的成本很高,不要輕易去操作DOM。尤其是React、vue等MV*框架的出現,數據驅動視圖的模式越發深入人心,jQuery時代提供的強大便...
摘要:作用域鏈用于表明上下文的執行順序。當前上下文執行完畢則出棧,執行下一個上下文。 從一個簡單的例子出發 先從一個簡單的例子出發(先不涉及異步),看看自己是否大致了解瀏覽器的執行機制: console.log(a); var a=1; function foo(a){ console.log(a); var a=2; console.log(a); } foo(a)...
摘要:瀏覽器創建進程的現象如圖所示默認的情況下打開瀏覽器,會創建以上進程。主要的三個為瀏覽器進程,進程,和一個默念的標簽頁進程。當我們瀏覽某個網頁的時候,引擎就會切換到這個網頁線程上運行。 1.瀏覽器創建進程的現象 showImg(https://segmentfault.com/img/bV42yw?w=687&h=370);如圖所示默認的情況下打開瀏覽器,會創建以上進程。主要的三個為:瀏...
摘要:瀏覽器的事件循環,前端再熟悉不過了,每天都會接觸的東西??梢钥吹剑^的并不是瀏覽器定義了哪些任務是,瀏覽器各個線程只是忠實地循環自己的任務隊列,不停地執行其中的任務而已。 瀏覽器的事件循環,前端再熟悉不過了,每天都會接觸的東西。但我以前一直都是死記硬背:事件任務隊列分為macrotask和microtask,瀏覽器先從macrotask取出一個任務執行,再執行microtask內的所...
閱讀 1618·2021-11-22 13:53
閱讀 2848·2021-11-15 18:10
閱讀 2755·2021-09-23 11:21
閱讀 2491·2019-08-30 15:55
閱讀 475·2019-08-30 13:02
閱讀 752·2019-08-29 17:22
閱讀 1659·2019-08-29 13:56
閱讀 3455·2019-08-29 11:31