1. 程式人生 > >至簡·js執行機制

至簡·js執行機制

快有一個月沒寫部落格了,真是造孽啊。雙十一也過去了,然鵝什麼都沒買。雙十一的結束,也預示著我長達十一個月的單身生活結束了,又要開始新的單身生活。最近這段時間一直在回顧反思,並學習一些新的東西。對於js的執行機制,也有了更深入的理解,之前寫過一篇文章,但是那篇文章對於js的底層原理並沒有深入的探討。
通過這幾個禮拜的學習,我重新去深入的瞭解底層的執行機制,在此作以分享。
先上圖:

精髓之圖!!!
在這裡插入圖片描述
這張圖看著“比較”複雜,我會對其中的部分進行詳細的講解,
我會從以下幾個部分對這張圖的內容進行解釋。
(1)程序與執行緒
(2)多程序的瀏覽器
(3)為什麼js單執行緒
(4)同步與非同步
(5)事件列表,事件佇列,ESC執行棧,EventLoop。
(6)其他部分
(7)ES6的中js處理機制的進階巨集任務與微任務。

對於這篇文章目前的規劃就是這些。接下來就來聊聊這些。
(1)程序與執行緒
為什麼要說這個,對於大多數人也許瞭解什麼是程序什麼是執行緒,即便如此還是有必要說一說的,並且很多人對於瀏覽器是型別的卻並不瞭解,所以有必要說一下。
程序是記憶體分配的最小單位
執行緒是cpu排程的最小單位
抽象派:
程序就是一個加工廠,每個加工廠裡的工人相當於執行緒,工人們相互協作完成任務,工廠內部的資源大家共同利用。工廠與工廠之間互相獨立。
每個工廠裡,都至少有一個工人。這就是程序與執行緒的原理。不做深究。
(2)多程序的瀏覽器
瀏覽器是多程序的,可以通過檢視瀏覽器的工作管理員瞭解,如下圖:

我們可以看到,瀏覽器是多程序的,並且每開啟一個新的tab頁,就會增加一個程序。
並且相同的tab頁的程序會被合併,這些可以自行驗證。
瀏覽的對程序主要有以下幾個:

  • Browser程序
  • render程序
  • GPU程序
  • 第三方外掛程序
  • 以及每個tab頁都是一個程序

(3)js單執行緒
我們所接觸的每個頁面是一個程序,而參與處理這個頁面的程序主要是由render程序完成的,render程序內部又是多執行緒的,主要分為以下幾個執行緒

  • 非同步的http請求執行緒;
  • 事件觸發執行緒
  • 定時器觸發執行緒
  • GUI渲染執行緒
  • js引擎執行緒

從上面可以看出來,瀏覽器只給了每個頁面處理js的一條執行緒,也就是js引擎執行緒,所以js是單執行緒的,這大概眾所周知了,可是為什麼js是單執行緒的,不見得重做周知,瀏覽器是多程序的,對於每個頁面的處理其實也是多執行緒的,但是對於js的處理,瀏覽器就給了一條執行緒。為什麼呢,瞭解一樣技術首先得從它的需求入手,不是說什麼什麼也可以什麼什麼,事情並沒有想的那麼簡單。對於js也不例外,js的設計初衷,是為了頁面互動,為了給瀏覽網頁的使用者帶來更良好的互動體驗,試想,如果js是多執行緒的,在同一時刻使用者操作網頁中的某個節點,但同時另一個執行緒要刪除這個節點,瀏覽器該當如何,既不能影響使用者正常體驗,又要刪除,並且誰能知道這個節點操作後,是否還會有後續操作。會帶來一系列的問題。所以作者,反其道而行之,不如化簡為一,只通過一條執行緒去處理。這就是為什麼js最初被設計成單執行緒的原因。當然省了事,就會帶來別的問題。
(4)同步與非同步
同步通俗的說就是一個時間段只能幹一件事,每個任務都必須在前一個任務執行結束後,在執行,就如同排隊一樣,來的早的先辦事,並且前一個任務不論耗時多久,後面的任務都得等,知道前面的任務結束。這樣就會造成一些問題,例如:如果一個頁面在執行某個任務,但是因此耗費了很長的執行時間,導致的問題就是,使用者不能進行別的操作,一直等待頁面的響應,這就會產生一種頁面卡死的現象,會帶來極差的使用者體驗。所以有了非同步的產生。
非同步是指:通俗的說就是同時幹多件事,不必等待之前的任務執行結束,後面的任務也可以去執行,這樣就可以解決同步所造成的資源浪費,並且對於js來說,也就能更有效的利用cpu的資源,js本質是直接耗費cpu資源的,這也是解釋性語言的特點,直接通過直譯器,再通過解碼器,有的甚至不通過解碼器例如V8引擎,就直接轉換為二進位制,供cpu進行運算。
但是js中是如何實現非同步的呢,按理來說js不是單執行緒嗎?我們要明確一點js是單執行緒,本質上並沒有改變。只是它實現非同步的方式有它的不同之處。
(5)事件列表,事件佇列,ESC執行棧,EventLoop。
接下來就是重頭戲了。這裡開始涉及到js的執行機制問題,所以敲黑板!!!
ESC執行棧:Execution Context即管理執行期上下文,是一個棧結構,所有的同步任務均由他管理執行,並且同步任務均屬於js主執行緒上的任務,由ESC執行棧完成呼叫,所有的其他的非同步任務基本上存在於事件列表中,事件列表與事件佇列並不相同,非同步任務主要分兩種,一種是定時器非同步任務,另一種是回撥任務,這兩種任務分別又不同的執行緒控制,定時器型別的非同步任務是由定時器觸發執行緒管理,非同步回撥任務是由事件處理執行緒進行管理的,接下來說說這些任務的運作方式。
:事件列表,存放所有的非同步任務,在事件列表中掛起執行,
:事件佇列,非同步任務執行得到響應後,會將對應的完成的非同步任務事件加至事件佇列中,之後被ESC執行棧呼叫。
整個的這一過程就是EventLoop的機制。
對於主執行緒上的任務,他們會進入ESC執行棧,受執行棧直接控制,然後對應的非同步任務,會被對應的執行緒進行管理,回撥型別的非同步任務會進入事件列表,此類任務會被掛起執行,等待I/O返回結果,結果返回後,會被推入事件列表,等待ESC執行棧中的任務執行完畢,然後進行EventLoop(事件迴圈),會到事件佇列中的頭部獲取等待執行的任務。對於定時器型別的非同步任務,直接由定時器事件觸發執行緒管理,定時到時後就會將任務直接推入事件佇列之中,如此迴圈往復,只要ESC執行棧中的任務結束,也就是主執行緒上的任務結束,就會進行事件迴圈,檢查事件列表,非同步任務,通過各自執行緒的管理進入事件列表,在沒有主執行緒任務時被ESC獲取,這就是最初的js執行機制。
對於執行棧部分的js程式碼解析過程,在本章節不重點討論。但是大概說一下,因為涉及到的知識點比較多如詞法作用域、scope、scope chain、prototype、prototype chain、VO、AO、以及Context(執行期上下文)、解析規則等等的問題,可以重新開幾個章節討論了,所以我只提幾個點,
像之前說的任務進入ESC執行棧中執行,說白了就是function裡的程式碼在ESC執行棧裡執行,但是在進入ESC執行棧執行之前還會有好多的事情發生,在函式建立的時候,就會發生一件事,即函式的作用域在它定義時就決定了,函式內部的屬性[[scope]]會儲存所有函式外部變數到其中,就是所有外部變數物件的層級連,然後在函式進入ESC時函式被啟用,別以為就這麼簡單,在啟用的時候,也會發生很多事,建立作用域、建立活動物件AO,並且初始化AO,加入形參,函式宣告,變數宣告,然後會就將AO物件壓入自身作用域鏈的頂端,然後才開始操作函式,函式執行的時候就是對AO物件進行修改,需要什麼先在自己的AO上找,找不到就沿著作用鏈向上找。最後函式執行結束,從ESC中彈出,被釋放。大致就是如此。

進階:

以上所講的js執行機制,很多人已經瞭解,當然很多人應該也不瞭解,當初我理解到此處的時候已經覺得上了一個層面,可是後來因為一些程式設計中遇到的例子,發現並不能解釋的通,就很奇怪,於是乎又是一對搜尋,不過說實話,搜尋能給的幫助太少了,多數文章千篇一律,一個抄一個,最後還是在基佬俱樂部guthub上得到了答案。

之前的執行機制,在我們遇到一般的程式可以解釋的通,並且之前的執行機制也並沒有錯,只是它還不夠完善。因為ES6中的Promise,可以看圖片中的例子,我都給到了,可以自己嘗試。Promise將js底層的任務劃分的更加明確,即微任務(microTask)和巨集任務(macroTask)的概念
巨集任務:就是ESC執行執行棧當中的任務,既包含當前的js主程式碼,定時器的非同步觸發任務等,因為最終都會進入ESC執行棧,都是從事件佇列中獲取的。
微任務:是更為細化的一種任務,它的執行比setTimeOut還要快,setTimeOut最小時間為4ms,即便設為0,這是最小的設定。例如Promise、Promise.nextTick等,

那他們是怎麼執行的呢?

巨集任務依然是在ESC執行棧中執行,與之前的同步任務,還有非同步任務的運作方式是一樣的,並且事件的迴圈也是一樣的,只是在每次重新向ESC執行棧中新增新任務時,會加一層操作,就是檢查微任務佇列。在程式碼執行過程中,所有遇到的微任務microTask,也會進入一個佇列中,這個微任務佇列直接由js引擎執行緒控制。每次ESC執行棧中的一個巨集任務執行結束,就會去檢查微任務佇列,尋找本輪是否還有微任務沒有執行,然後執行微任務。只有本輪的巨集任務和微任務都執行結束,然後開始檢查渲染,此時由GUI執行緒接管渲染。渲染完畢後,就又會由JS引擎接管,開始下一輪的巨集任務。

問題到了此處,已經又是一個層面了,是不是覺得我逼話連篇,嘿嘿嘿,但是,理解到此處,卻是豁然開朗的感覺,我也懶的敲程式碼示例了,理解並畫出那張圖花了不少時間,並且也把程式碼示例附在了圖上,可以自己嘗試。唉…,底層的東西太多,太深了,我還是知道的少。繼續吧,不要鬆懈。

相關推薦

·js執行機制

快有一個月沒寫部落格了,真是造孽啊。雙十一也過去了,然鵝什麼都沒買。雙十一的結束,也預示著我長達十一個月的單身生活結束了,又要開始新的單身生活。最近這段時間一直在回顧反思,並學習一些新的東西。對於js的執行機制,也有了更深入的理解,之前寫過一篇文章,但是那篇文章

js --- 執行機制

循環 gpo 可執行 pad loop 同步任務 16px 觸發 pos 1. JS為什麽是單線程的?  JS最初被設計用在瀏覽器中,那麽想象一下,如果瀏覽器中的JS是多線程的。 那麽現在有2個進程,process1 process2,由於是多進程的JS,所以他們對同一個

js執行機制

分發 sleep 代碼執行 過程 rom spa set 是否 引擎 1. 關於javascript js是一門單線程語言,一切js版的‘多線程’都是用單線程模擬起來的。 2. js事件循環 將任務分為2類:同步任務、異步任務 同步任務進入主線程,異步任務

JS執行機制(學習筆記)

程序和執行緒: 程序是一個工廠,工廠之間相互獨立 執行緒是工廠中的工人,多個工人(執行緒)協作完成任務 工廠內有一個或多個工人-工人之間共享空間 工廠的資源->系統分配的記憶體(獨立的一塊記憶體) 工廠之間相互獨立->程序之間相互獨立 多個工人協作完成任務-&

js執行機制及非同步程式設計(一)

相信大家在面試的過程中經常遇到檢視執行順序的問題,如setTimeout,promise,async await等等,各種組合,是不是感覺頭都要暈掉了,其實這些問題最終還是考察大家對js的執行機制是否掌握牢固,對promise,async的原理是否掌握,萬變不離其宗,這次就來徹底搞懂它。 1 js引擎

研究JS執行機制之重新認識JavaScript(1) ———— 認識Js執行上下文與執行機制

關於這個系列 javascript語言有很多奧祕,譬如其執行機制,內部原理,在歷史的這一段日子裡,它不僅帶來了複雜的互動效果和充分的效能效益,而且吸引了越來越多的開發者加入其中,但是隨著時間程序的發展,很多時候開發者們因為花樣繁複的JS庫與框架而忽略了JS本身的內部機制。無可厚非,這樣做是增加了效率,但是隨著

淺談js執行機制

關於js執行機制,老早之前就一直想寫篇文章做個總結,因為和js執行順序的面試題碰到的特別多,每次碰到總是會去網上查,沒有系統地總結,搞得每次碰到都是似懂非懂的感覺,這篇文章就係統的總結一下js執行機制。 任務佇列 大家都知道js最大的特點就是單執行緒執行,這就是為什麼js簡單易學的一個重要原

前端安全、瀏覽器渲染機制js執行機制、頁面效能、錯誤監控

一、安全類1、csrf:跨站請求偽造;原理:(1) 使用者C開啟瀏覽器,訪問受信任網站A,輸入使用者名稱和密碼請求登入網站A;(2)在使用者資訊通過驗證後,網站A產生Cookie資訊並返回給瀏覽器,此時使用者登入網站A成功,可以正常傳送請求到網站A; (3)使用者未退出網站A

JS執行機制詳解

你會 成了 分類 event col 這也 obs play 改變 通過結果倒推過程是我們常用的思考模式,我在上一篇學習promise筆記中,有少量關於promise執行順序的例子,通過倒推,我成功讓自己對於js執行機制的理解一塌糊塗,js事件機制,事件循環是面試常考的點,

JS】JavaScript引擎的內部執行機制

under scrip str tro blog rip 回調函數 ron span  近期在復習JavaScript,看到setTimeout函數時。想起曾經剛學時,在一本書上看過setTimeout()裏的回調函數執行的間隔時間

[轉]JS 引擎的執行機制

wan queue 多線程 .html 單擊事件 語句 eve resolve title ------------------------------------------------------ JS 引擎的執行機制 關於JS引擎的執行機制,首先牢記2點:

js為什麽是單線程的?10分鐘了解js引擎的執行機制

容易 等於 bsp -m 深入理解 block 順序 dom 依次 深入理解JS引擎的執行機制 1.JS為什麽是單線程的? 為什麽需要異步? 單線程又是如何實現異步的呢? 2.JS中的event loop(1) 3.JS中的event loop(2) 4.說說s

淺析JS異步執行機制

一個隊列 http請求 調度 等待 __name__ 服務端 nco sta req 前言 JS異步執行機制具有非常重要的地位,尤其體現在回調函數和事件等方面。本文將針對JS異步執行機制進行一個簡單的分析。 從一份代碼講起 下面是兩個經典的JS定時執行函數,這兩個函數的區別

從Event Loop談JS執行機制

這裡主要是結合Event Loop來談JS程式碼是如何執行的。   讀這部分的前提是已經知道了JS引擎是單執行緒,而且這裡會用到前面說的的幾個概念:(如果不是很理解,可以回頭溫習) JS引擎執行緒 事件觸發執行緒【輪訓】 定時觸發器執行緒 然後再理解一個概念:

JS 引擎的執行機制

首先需明白兩點: JS是單執行緒語言 JS的Event Loop是JS的執行機制。深入瞭解JS的執行,就等於深入瞭解JS裡的event loop (1) JS為什麼是單執行緒的? JS最初被設計用在瀏覽器中,那麼想象一下,如果瀏覽器中的JS是多執行緒的。 場景描述:

從template到DOM(Vue.js原始碼角度看內部執行機制)

寫在前面 這篇文章算是對最近寫的一系列Vue.js原始碼的文章(https://github.com/answershuto/learnVue)的總結吧,在閱讀原始碼的過程中也確實受益匪淺,希望自己的這些產出也會對同樣想要學習Vue.js原始碼的小夥伴有所幫助。之前這篇文章同樣在我司(大搜車)的

深入理解js引擎的執行機制

深入解讀js引擎的執行機制 最近在反省,很多知識都是隻會用,不理解底層的知識。所以在開發過程中遇到一些奇怪的比較難解決的bug,在思考的時候就會收到限制。所以,在這裡一點一點補充基礎知識吧。 在閱讀之前,請先記住兩點: js是單執行緒語言 js的Event Loop

js基礎總結(二)js執行機制

執行結果首先全部輸出first,然後全部輸出second。 再來看一道題: 應該是依次彈出4444 這裡考察的都是JS的執行機制。事件click, focus等等,定時器setTimeout和setInterval,ajax都會觸發非同步,屬於非同步任務。js是單執行緒的一個時

前端讀者 | 由setTimeout引發的JS引擎執行機制的研究

本文來自 @xiaoyuze88 連結:http://xiaoyuze88.github.io/ 太久沒碰程式碼了,那天想到關於迴圈呼叫setTimeout實現每隔一秒輸出遞增的數的那個問題,搞了搞,發現很多概念模糊了,在此總結下。 所謂的迴圈呼叫setTimeout實現遞增輸出,就是說用for

JS引擎的執行機制

來源 首先,請牢記2點: (1) JS是單執行緒語言 (2) JS的Event Loop是JS的執行機制。深入瞭解JS的執行,就等於深入瞭解JS裡的event loop 技術的出現,都跟現實世界裡的應用場景密切相關的。 同樣的,我們就結合現實場景,來回答這三個問