日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

从Javascript单线程谈Event Loop

發布時間:2023/12/31 java 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从Javascript单线程谈Event Loop 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

假如面試回答js的運行機制時,你可能說出這么一段話:“Javascript的事件分同步任務和異步任務,遇到同步任務就放在執行棧中執行,而碰到異步任務就放到任務隊列之中,等到執行棧執行完畢之后再去執行任務隊列之中的事件。”但你能說出背后的原因嗎?

?

先理解相關概念

線程與進程

進程:是系統資源分配和調度的單元。一個運行著的程序就對應了一個進程。一個進程包括了運行中的程序和程序所使用到的內存和系統資源。

線程:線程是進程下的執行者,一個進程至少會開啟一個線程(主線程),也可以開啟多個線程。

?

同步和異步

同步和異步關注的是消息結果通信機制

同步發出調用,在沒有得到結果前,該調用不返回。但是一旦調用返回,就得到返回值

異步發出調用后,調用直接返回,沒有返回結果。但結果由回調函數給出,至于什么時候給出,不知道。(這個回調函數肯定是在當前js執行完后才執行)

?

阻塞與非阻塞

阻塞和非阻塞關注的是程序在等待調用結果時的狀態.

阻塞調用調用結果返回之前,當前線程被掛起。調用線程只有在得到結果后才會返回。
非阻塞調用在不能立刻得到結果之前,該調用不會阻塞當前線程。

?

為什么JavaScript是單線程?

JavaScript是單線程,程序按照順序排列,前面的必須處理好,后面的才會執行。JavaScript的設計初衷是作為瀏覽器腳本語言,主要是簡單用戶交互、操作DOM等,所以這門語言要圍繞單線程來設計,否則出現復雜的同步問題。

由于JavaScript是單線程的,對于耗時的或者時間不確定的操作,我們可以使用異步編程,因為異步可以實現非阻塞操作。當然也可以用HTML5標準的Web Worker。本文不作討論,詳細參考MDN文檔:點擊這里

既然js是單線程執行的,那誰去輪詢大的任務隊列?這不矛盾了嗎?

?

Js的單線程與異步矛盾嗎?

不矛盾!!!首先記住這句話:Js執行是單線程,但瀏覽器是多線程。

1)JS的單線程

一個瀏覽器進程中只有一個JS的執行線程,同一時刻內只會有一段代碼在執行

?

2)瀏覽器多線程

查閱資料,有些文章也說是模塊,本文就以瀏覽器多線程來說,它常駐線程:

渲染引擎線程:負責頁面的渲染

JS引擎線程:負責JS的解析和執行(本文說的主線程就指js引擎線程)

定時器觸發線程:處理定時事件,比如setTimeout, setInterval

事件觸發線程:處理DOM事件

異步http請求線程:處理http請求

? ? ? ? ......

瀏覽器Js使用場景,瀏覽器本身是典型的 GUI 工作線程(GUI 工作線程在絕大多數系統中都實現為事件處理,避免阻塞交互)。故瀏覽器是事件驅動的(Event driven),瀏覽器中很多行為是異步,會創建事件并放入任務隊列中。

由于Javascript 是單線程,它需要借助異步完成耗時或者時間不確定的操作,這些操作由瀏覽器的其它線程執行,這形成了異步事件驅動。異步事件驅動往往由瀏覽器的兩個或以上常駐線程共同完成的例如ajax異步請求是由JS執行線程和異步http請求線程事件觸發線程共同完成的。

?

事件循環機制(Event Loop)

相關概念

函數調用形成一個棧幀。

1 function foo(b) { 2 let a = 10; 3 return a + b + 11; 4 } 5 6 function bar(x) { 7 let y = 3; 8 return foo(x * y); 9 } 10 11 console.log(bar(7));

當調用 bar?時,創建了第一個幀 ,幀中包含了 bar?的參數和局部變量。

bar?調用 foo?時,第二個幀就被創建,并被壓到第一個幀之上,幀中包含了 foo 的參數和局部變量。

foo 返回時,最上層的幀就被彈出棧(剩下 bar?函數的調用幀 )。

bar?返回的時候,棧就空了。

對象被分配在一個堆中,一個用以表示一個內存中大的未被組織的區域。

每一個線程只有一個棧,每一個程序只有一個堆。

隊列

一個 JavaScript 運行時包含了一個待處理的消息隊列。每一個消息都與一個函數相關聯。

當棧為空時,從隊列中取出一個消息進行處理。這個處理過程包含了調用與這個消息相關聯的函數。

當棧再次為空的時候,也就意味著消息處理結束。

?

任務隊列消息隊列

任務隊列是一個先進先出的數據結構,當主線程執行棧一清空,任務隊列的回調函數就自動進入主線程。任務分成兩種

1同步任務在主線程上排隊執行的任務只有執行完前任務,才能執行后一個任務。

2、異步任務:該任務不進入主線程、而進入任務隊列。當執行棧清空后,才去執行任務隊列中的任務。

?

異步執行的運行機制

由于JavaScript只能一次執行一段代碼(由于其單線程性質),這些代碼塊中的每一個都“阻止”其他異步事件的進度。這意味著當異步事件發生時(如鼠標點擊,定時器觸發或XMLHttpRequest完成),它將排隊等待稍后執行(這種排隊實際發生的確定會因瀏覽器到瀏覽器而異)。

1、所有同步任務都在主線程上執行,形成一個執行棧。

2、當遇到異步任務時(IO設備操作等,就在任務隊列中添加一個事件,這個事件對應著該異步任務的回調函數。

3、執行棧中的所有同步任務執行完畢,系統就會讀取任務隊列,進入執行棧,開始執行。

4、主線程不斷重復第三步。這就形成了事件循環

結論:Javascript的事件分同步任務和異步任務,遇到同步任務就放在執行棧中執行,而碰到異步任務就放到任務隊列之中,等到執行棧執行完畢之后再去執行任務隊列之中的事件。

?

事件和回調函數的概念必要說明

  • 工作線程:是本文對除了js引擎線程之外的其它線程的統稱
  • 回調函數:在一個函數中調用另外一個函數。這里指異步場景下為了非阻塞那些被主線程掛起來的代碼。
  • ?主線程讀取任務隊列,就是讀取里面有哪些事件,執行對應的回調函數。

工作線程完成一項任務,就任務隊列中添加一個事件。這里的完成任務是指完成操作(click、mouse、touchajax的數據完全請求回來......),并非指執行它的回調函數

a.onclick = function () {console.log("roro") }

如上段代碼,僅是操作了click,但并沒有執行回調函數打印roro

?

事件循環

事件循環是:主線程重復從任務隊列中取消息(事件),執行對應回調函數的過程。

?

?

上圖中,主線程運行的時候,產生堆(heap)和棧(stack),棧中的代碼調用各種外部API,它們在任務隊列中加入各種事件(clickloaddone)。只要執行引擎棧棧中的代碼執行完畢,主線程就會去讀取任務隊列,依次執行那些事件所對應的回調函數。

?

定時器

首先參考這篇外國人的文章:how-javascript-timers-work,定時器的執行原理及細節。

setTimeout()怎么執行?

setTimeout(function () {console.log('a'); }, 5000)

? ? ? ?Javascript執行引擎主線程運行的時候,產生堆和棧。程序中代碼依次進入棧中等待執行,當調用setTimeout()方法時,瀏覽器的定時器線程下處理延時方法,當setTimeout方法執行5秒后到達觸發條件方法被添加到用于回調的任務隊列。

? ? ? ?當執行引擎的執行棧為空,執行引擎開始輪詢檢查任務隊列是否有任務需要被執行,檢查到已經符合執行條件的延時方法,將延時方法console.log('a')壓入執行棧引擎發現調用了log()方法,于是又將log()方法入棧。然后對執行棧依次出棧執行,輸出‘a’,清空執行棧,整個執行完畢。

?

setTimeout(fn,0)是立即執行嗎?

javascript權威指南中:當setTimeout的延遲時間設置為0的時候,回調函數不會馬上執行,而是進入?事件隊列。

?

btn.onclick = function () {setTimeout(function () {console.log('a')}, 0); }

?

setTimeout(fn,0)的含義是:指定某個任務在主線程的空閑時間,盡可能早執行。它被添加進任務隊列,因此要等到同步任務和任務隊列前一個事件都處理完,才執行。

HTML5標準規定了setTimeout()的第二個參數的最小值不得低于4ms,如果低于這個值,就會自動增加。老版本的瀏覽器允許最短間隔設為10ms。詳細參考MDN文檔:最小延遲和超時嵌套

所以setTimeout(fn,0)并不是立即執行。假若你想實現0ms 的timeout可以用?window.postMessage(),本文不作討論。

?

兩道經典的面試題:

一)以下代碼輸出什么?

function foo() {console.log('a')setTimeout(function () {console.log('b');}, 500) }for (let i = 0; i < 10000; i++) {foo() }

執行結果首先全部輸出a,中間等待500ms,然后全部輸出b。上圖是個人理解,不恰當的地方請指出!

?

二)ajax異步請求是否真的異步?

1、JS的執行線程(主線程)發起異步請求瀏覽器會開一條新的HTTP請求線程來執行請求,繼續執行中剩下的任務,

2、在新線程(HTTP請求線程)中,在執行請求的同時,瀏覽器會正常處理其他任務的執行。

3、在未來的某一時刻當數據完全請求回來以后事件觸發線程監視到之前發起的HTTP請求已完成,會將指定的回調函數放入任務隊列中。

4當瀏覽器執行棧空閑時,去掃描任務隊列中的回調函數,依次壓入執行棧中處理。

所以:ajax請求是異步。由瀏覽器新開一個線程請求,事件回調的時候放入Event loop任務隊列等候處理。詳細的例子,可以參考MDN文檔對ajax的描述:同步和異步

?

誤解:事件循環類似?;蜿犃?/strong>

這里順帶提一下:事件循環雖然涉及到類似隊列的結構,并不是采用棧的方式處理任務。事件循環作為一個進程被劃分為多個階段,每個階段處理一些特定任務,各階段輪詢調度。這些階段可以是定時器處理,dom事件處理,ajax異步處理......

?

結語

JavaScript引擎只有一個線程,強制異步事件排隊等待執行,Javascript語言的事件循環,是瀏覽器的處理和行為。另外,本文是我個人的學習筆記,通篇結合個人的理解,在某些地方表述不嚴謹,如有錯誤,希望指出。

轉載于:https://www.cnblogs.com/LIUYANZUO/p/7353547.html

總結

以上是生活随笔為你收集整理的从Javascript单线程谈Event Loop的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。