<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      事件循環

      事件循環與瀏覽器有關,需要先了解其進程模型。

      瀏覽器的進程模型

      進程

      程序運行需要其專屬的內存空間,用于存儲變量、執行函數等操作,可以將這塊內存空間簡單地理解為進程。

      每個應用至少有一個進程,進程之間相互獨立,即使要通信,也需要雙方同意。

      process_thread

      線程

      有了進程后,就可以運行程序的代碼了,由線程運行代碼。

      一個進程至少有一個線程,在進程開啟后會自動創建一個線程來執行代碼,稱為主線程

      如果主線程結束了,那么進程就結束了。

      如果程序需要同時執行多塊代碼,主線程就會啟動更多的線程來執行代碼,所以一個進程中可以包含多個線程。

      瀏覽器的進程和線程

      瀏覽器是一個多進程多線程的應用程序。

      為了避免相互影響,為了減少連環崩潰的幾率,當啟動瀏覽器后,它會自動啟動多個進程。

      現代瀏覽器已經非常復雜了,復雜程度在向操作系統靠近。

      image-20231012141200080

      啟動chrome瀏覽器,打開其任務管理器:

      image-20231012141407647

      可以發現盡管沒有訪問任何網頁,也有一些進程是自動啟動的

      打開一個新的標簽頁,這里打開了百度,可以發現不同的標簽頁屬于不同的進程

      image-20231012142226444

      其中,最主要的進程有:

      1. 瀏覽器進程

        瀏覽器進程是最先啟動的進程,其它進程由它啟動。

        主要負責界面顯示、用戶交互、子進程管理等。瀏覽器進程內部會啟動多個線程處理不同的任務。

        這里的界面顯示不是指網頁的內容渲染,而是指瀏覽器的界面,比如瀏覽器的頭部:

        image-20231012142510968

        用戶交互是指用戶在瀏覽器上的點擊、鍵盤、滾輪等操作,瀏覽器需要監聽這些用戶交互操作。

        子進程管理包含網絡進程、渲染進程等等。

      2. 網絡進程

        負責加載網絡資源。網絡進程內部會啟動多個線程來處理不同的網絡任務。

      3. 渲染進程

        渲染進程啟動后,會開啟一個渲染主線程,主線程負責執行HTML、CSS、JS代碼。

        默認情況下,瀏覽器會為每個標簽頁開啟一個新的渲染進程,以保證不同的標簽頁之間不相互影響。

        這種模式可能會在以后的版本被替換掉,因為每個標簽頁都開啟一個新渲染進程會導致chrome成為“內存殺手”。——2023.10

        Chromium Docs - Process Model and Site Isolation (googlesource.com)

        ??這個文檔提到了,以后可能會發展成“一個站點對應一個進程”。

        例如:用戶在使用淘寶的時候,首先打開了淘寶首頁,然后搜索商品進入了商品搜索頁,點擊商品進入商品詳情頁,盡管有三個標簽頁,但是都是同一站點,只對應一個進程。

        當然,這是以后的發展規劃,了解即可。

      關于瀏覽器的進程模型就介紹到這里,本文主要是寫事件循環,而事件循環就發生在渲染主線程中。

      渲染主線程

      渲染主線程是瀏覽器中最繁忙的線程,需要它處理的任務包括但不限于:

      • 解析HTML:比如解析標記語言的語法。
      • 解析CSS:比如解析樣式表的語法。
      • 計算樣式:比如將em%等單位換算成px,以及重復的屬性如何考慮其優先級等等。
      • 布局:每個元素的寬高、位置,都需要計算,統稱為幾何信息。
      • 處理圖層:與z-index有關,也和元素的內容與其background的繪制有關。
      • 每秒把頁面繪制60次:FPS
      • 執行全局JS代碼
      • 執行事件處理函數
      • 執行計時器的回調函數
      • ......

      為什么渲染進程不適用多個線程來處理這些事情?

      瀏覽器渲染進程通常是單線程的,這是因為多線程處理可能會引發很多問題。以下是一些可能的問題:

      競態條件:多線程處理會導致訪問共享內存的競爭條件,可能導致數據不一致和死鎖等問題。
      同步問題:多線程需要進行同步,避免數據競爭和死鎖,這會增加代碼的復雜度和開銷。
      安全問題:多線程可能會存在安全漏洞,如數據泄露、內存溢出等問題。
      性能問題:多線程處理可能會導致過多的上下文切換和內存消耗,從而降低程序的性能和穩定性。

      相比之下,單線程處理有以下優點:

      簡單易用:單線程的處理方式更加簡單易用,開發人員不需要考慮多線程處理中的競態條件、同步問題和安全問題。

      可靠穩定:單線程處理避免了多線程處理中的死鎖和資源爭用等問題,從而提高了程序的可靠性和穩定性。

      高效節省:單線程處理可以避免多線程處理中的上下文切換和內存消耗等問題,從而提高了程序的性能和節省了系統資源。

      既然主線程非常繁忙,那么首先需要解決的問題就是:應該如何調度任務?

      比如:

      • 正在執行一個JS函數,執行到一半的時候用戶點擊了按鈕,是否應該立即去執行點擊事件的處理函數?
      • 正在執行一個JS函數,執行到一半的時候某個計時器到達了時間,是否應該立即去執行其回調?
      • 當“用戶點擊事件”,與“計時器到達時間”同時發生,應該先處理哪個任務?
      • ......

      渲染主線程的處理方式是:排隊(消息隊列 Message queue)。

      image-20231012160858665

      image-20231012163614949

      1. 在最開始的時候,渲染主線程會進入一個無限循環;

        可以在github上找到chromium的源碼,在消息隊列的相關實現代碼中可以看到一個無限循環:

        ??message_dump_default.cc

      image-20231012155643830

      chromium和chrome的關系:

      • chrome是基于chromium的、功能更豐富的瀏覽器;
      • chrome不開源,chromium開源。
      1. 每一次循環會檢查消息隊列中是否有任務存在。
      • 如果有,就取出第一個任務執行,執行完一個后進入下一次循環;
      • 如果沒有,則進入休眠狀態。
      1. 其它所有線程(包括其它進程的線程)可以隨時向消息隊列添加任務。新任務會加到消息隊列的末尾。在添加新任務時,如果主線程是休眠狀態,則會將其喚醒以繼續循環拿取任務。

      這樣一來,就可以讓每個任務有條不紊地、持續地進行下去了。

      整個過程,被稱之為事件循環(消息循環)。

      • 在w3c的標準中被稱為:event loop事件循環
      • 在google的標準中被稱為:message loop消息循環

      回過頭來看上文提到的一個問題:

      正在執行一個JS函數,執行到一半的時候用戶點擊了按鈕,是否應該立即去執行點擊事件的處理函數?

      :此時應該將點擊事件的處理函數推到消息隊列中,不影響JS函數的執行。

      相關問題

      異步是什么?

      代碼在執行過程中,會遇到一些無法理解處理的任務,比如:

      • 計時完成后需要執行的任務——setTimeoutsetInterval
      • 網絡通信完成后需要執行的任務——XHRFetch
      • 用戶操作后需要執行的任務——addEventListener

      如果讓渲染主線程等待這些任務的時機到達,就會導致主線程長期處于阻塞的狀態,從而導致瀏覽器卡死

      image-20231012161901459

      如上圖,在主線程中使用setTimeout(fn, 3000)將回調函數與計時時長告知計時線程,計時線程開始計時等待。

      此時,如果主線程跟隨一起等待,則是同步,唯一的好處是時間線同步,但是好處遠遠小于壞處,非常浪費時間。

      計時線程的底層實現其實是調用了操作系統提供的接口。其實現十分復雜。

      渲染主線程承擔著極其重要的工作,無論如何都不能阻塞。

      如果因為上述三種任務阻塞了主線程,那么消息隊列中可能存在著渲染任務無法執行,也可能用戶觸發了點擊事件,但是沒有被執行,從用戶角度來說就是“頁面卡死了”。

      因此,瀏覽器選擇了異步來解決這個問題。

      image-20231012164754362

      如上圖,主線程在調用了setTimeout之后,其實只是起到了一個預約的作用,最后是由計時線程將任務交給消息隊列。

      使用異步的方式,渲染主線程不會阻塞,不斷地拿取消息隊列里的任務去執行,遇到計時器、網絡請求、事件監聽,則交給其它線程,渲染主線程從消息隊列中獲取下一個任務繼續工作。

      面試題:如何理解JS的異步?

      參考答案:
      JS是一門單線程的語言,這是因為它運行在瀏覽器的渲染主線程中,而渲染主線程只有一個。
      而渲染主線程承擔著諸多的工作,渲染頁面、執行JS都在其中運行。
      如果使用同步的方式,就極有可能導致主線程產生阻塞,從而導致消息隊列中的很多其他任務無法得到執行。
      這樣一來,一方面會導致繁忙的主線程白白的消耗時間,另一方面導致頁面無法及時更新,給用戶造成卡死現象。
      所以瀏覽器采用異步的方式來避免。具體做法是當某些任務發生時,比如計時器、網絡、事件監聽,主線程將任務交給其他線程去處理,自身立即結束任務的執行,轉而執行后續代碼。當其他線程完成時,將事先傳遞的回調函數包裝成任務,加入到消息隊列的未尾排隊,等待主線程調度執行。
      在這種異步模式下,瀏覽器永不阻塞,從而最大限度的保證了單線程的流暢運行。

      JS為何會阻礙渲染?

      首先看示例代碼:

      btn.onclick = function(){
          // 修改DOM節點的內容
          p.innerText = "hello world";
          // 模擬很耗時長的JS代碼,這里等了3秒
          start = Date.now()
          while(Date.now()-start<3000){}
      }
      
      • 錯誤的理解:點擊按鈕之后,先修改DOM節點文本,再等待3秒;
      • 實際現象:點擊按鈕之后,會先阻塞等待3秒,然后才能看到DOM節點的文本更新。

      具體的流程如下

      1. 首先,主線程解析全局JS代碼,解析到這一段代碼的時候,檢測到是事件綁定,將函數轉交給交互線程
      2. 交互線程監聽按鈕點擊,當監聽到按鈕被點擊之后,將函數包裝成任務推送到消息隊列尾部。
      3. 當其它任務被渲染主線程執行完成之后,該任務被主線程獲取,執行內部的代碼。
      4. 修改DOM節點的操作,屬于繪制任務,會被包裝成任務推送到消息隊列尾部。
      5. 與此同時,上述代碼的這個任務還在執行中,while語句阻塞3秒。
      6. 3秒之后,該任務執行完成。渲染主線程繼續從消息隊列中獲取任務,當獲取到繪制任務并執行的時候,界面才會更新。

      簡單的理解

      • 在瀏覽器上,JS解析和頁面渲染都在渲染主線程上被執行,同一時間只能完成一個任務。

      • 在JS中生成的渲染任務,需要JS執行完成之后才會被更新到頁面上。

      任務有優先級嗎?

      任務沒有優先級,在消息隊列中先進先出。但消息隊列是有優先級的

      根據W3C的最新解釋:

      • 每個任務都有一個任務類型,同一個類型的任務必須在一個隊列,不同類型的任務可以分屬于相同的隊列(即一個隊列可以用于存放多種類型的任務)。

        在一次事件循環中,瀏覽器可以根據實際情況從不同的隊列中取出任務執行。不同瀏覽器或者同一瀏覽器的不同版本的做法可能不同。

        在chromium的源碼中,我們可以找到??task_type.h,其中聲明了許多任務類型:

      • 瀏覽器必須準備好一個微隊列(microtask queue),微隊列中的任務優先所有其它任務執行。

        ??HTML Standard (whatwg.org)

      隨著瀏覽器的復雜度急劇提升,W3C不再使用宏隊列的說法。

      在目前chrome的實現中,至少包含了下面的隊列:

      • 延時隊列:用于存放計時器到達后的回調任務,優先級
      • 交互隊列:用于存放用戶操作后產生的事件處理任務,優先級
      • 微隊列:用戶存放需要最快執行的任務,優先級最高

      添加任務到微隊列的主要方式主要是使用 PromiseMutationObserver

      例如:

      // 立即把一個函數添加到微隊列
      Promise.resolve().then(函數)
      

      瀏覽器還有很多其它的隊列,與前端的開發關系不大。


      面試題:闡述一下 JS 的事件循環

      參考答案

      事件循環又叫做消息循環,是瀏覽器渲染主線程的工作方式。
      在 Chrome 的源碼中,它開啟一個不會結束的for循環,每次循環從消息隊列中取出第一個任務執行,而其他線程只需要在合適的時候將任務加入到隊列未尾即可。
      過去把消息隊列簡單分為宏隊列和微隊列,這種說法目前已無法滿足復雜的瀏覽器環境,取而代之的是一種更加靈活多變的處理方式。
      根據W3C官方的解釋,每個任務有不同的類型,同類型的任務必須在同一個隊列,不同的任務可以屬于相同的隊列。不同任務隊列有不同的優先級,在一次事件循環中,由瀏覽器自行決定取哪一個隊列的任務。但瀏覽器必須有一個微隊列,微隊列的任務一定具有最高的優先級,必須優先調度執行。


      面試題:JS 中的計時器能做到精準計時嗎?為什么?

      參考答案

      不行,因為:

      1. 計算機硬件沒有原子鐘,無法做到精確計時;
      2. 操作系統的計時函數本身就有少量偏差,由于 JS 的計時器最終調用的是操作系統的函數,也就攜帶了這些偏差;
      3. 按照W3C的標準,瀏覽器實現計時器時,如果嵌套層級超過5層,則會帶有4毫秒的最少時間,這樣在計時時間少于4毫秒時又帶來了偏差;
      4. 受事件循環的影響,計時器的回調函數只能在主線程空閑時運行,因此又帶來了偏差。

      補充:

      1. setTimeoutsetInterval的最終實現調用的操作系統的API,不同的操作系統的實現不同。

      2. setTimeout嵌套層級(5層)與4毫秒的知識點:

        chromium關于計時器的源碼中可以找到:

        根據w3c的標準,當嵌套層級超過五層,定時器的時間如果低于4ms,會被提升至4ms。

      例題

      可以在草稿紙上寫下渲染主線程微隊列延時隊列,并模擬任務在其中的變化。

      例題1:下述代碼的輸出順序是?

      function a(){
          console.log(1);
          Promise.resolve().then(function(){
              console.log(2);
          });
      }
      
      setTimeout(function(){
          console.log(3);
          Promise.resolve().then(a);
      }, 0);
      
      Promise.resolve().then(function(){
      	console.log(4);   
      });
      
      console.log(5);
      

      例題 1 答案

      5
      4
      3
      1
      2
      

      例題2:下述代碼的輸出順序是?

      function a(){
      	console.log(1);
          Promise.resolve().then(function{
          	console.log(2);                       
          });
      }
      
      setTimeout(function(){
          console.log(3);
      }, 0);
      
      Promise.resolve().then(a);
      
      console.log(5);
      

      例題 2 答案

      5
      1
      2
      3
      

      總結

      • 單線程是異步產生的原因;
      • 事件循環是異步的實現方式。
      posted @ 2023-10-14 17:50  feixianxing  閱讀(202)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产一级二级三级毛片| 亚洲精品免费一二三区| 女人香蕉久久毛毛片精品| 亚洲愉拍一区二区三区| 亚洲成在人线在线播放无码| 日韩大片看一区二区三区| xxxx丰满少妇高潮| 色综合久久一区二区三区| 少妇被粗大的猛烈进出69影院一 | 国产精品九九九一区二区| 免费看男女做好爽好硬视频| 日韩深夜福利视频在线观看| 国产一区二区三区综合视频| 国产精品福利自产拍在线观看| 久久se精品一区精品二区国产| 色悠悠国产精品免费观看| 亚洲欧美自偷自拍视频图片| 亚洲色偷偷色噜噜狠狠99| 国产午夜福利视频在线| 国产一区二区三区精美视频| 欧美情侣性视频| 国产精品一区二区日韩精品| 深夜精品免费在线观看| 欧美大胆老熟妇乱子伦视频| 国产精品女人毛片在线看| 少妇高清一区二区免费看| 欧美人妻在线一区二区| 男女啪啪高潮激烈免费版| 亚洲大尺度一区二区av| 亚洲色成人一区二区三区人人澡人人妻人人爽人人蜜桃麻豆 | bt天堂新版中文在线| 免费人成年激情视频在线观看| 九九热视频在线精品18| 香港日本三级亚洲三级| 精品国产迷系列在线观看| 亚洲色大成网站WWW尤物| 免费人妻av无码专区| 国产啪视频免费观看视频| 四虎国产成人永久精品免费| 亚洲精品国产一区二区三区在线观看| 边添小泬边狠狠躁视频|