1. 程式人生 > 程式設計 >JavaScript執行機制例項分析

JavaScript執行機制例項分析

本文例項講述了JavaScript執行機制。分享給大家供大家參考,具體如下:

第一次寫部落格

目前研一第二學期,大二開始入門前端,然而長久以來都是對於框架的簡單呼叫,並未對其進行深入研究,因此,這個部落格是作為自我督促的開始。這篇部落格的內容源於前段時間寫一個微信小程式前端,發現頁面的渲染順序總與自己的預想相違背,因此近期看了一些關於JavaScript執行機制的部落格及文件,有了一些基本的框架,接下來就來詳細看一下我所瞭解到的內容。

JavaScript執行順序

首先,JavaScript是按照順序,一行一行執行的,且JS只有一條執行緒,即不可能進行兩條程式碼同時執行,也就是說,在一條程式碼執行時,它後面的所有程式碼都需要等待,直到該程式碼執行結束,後面的才能繼續執行。如果是這樣,就會導致使用者體驗度極其不好,例如一個請求傳送給伺服器,後續程式碼就會一直等待,直到伺服器返回結果,使用者才能進行新的操作。

這又是怎麼回事呢?

詳細解釋JavaScript執行機制

JavaScript執行棧

JavaScript是以壓棧的方式進行程式碼的執行的,一開始執行時棧內為空,當執行開始,JS引擎會將程式碼放入棧底,若該程式碼包含其他函式的呼叫,則將被呼叫的函式放在棧頂,若該程式碼未包含其他函式的呼叫,則執行該函式,執行完成後出棧,以此類推,最終直到棧為空。

JavaScript的同步任務和非同步任務

事實上,真正的JS內部分為同步任務和非同步任務,然而這並沒有改變JS單執行緒的特徵。

  • 同步任務:執行後直接返回結果,例:console.log();c = a + b
  • 非同步任務:執行後無法立刻返回結果,需要等待一定時間,才能執行回撥函式,對返回結果進行操作

系統來說,JS存在一個主執行緒,它會首先執行所有同步任務,而非同步任務都會先進行註冊,然後主執行緒不會等待非同步任務執行結果的返回,而是繼續執行下面的同步任務(在此過程中,如果非同步任務返回結果,接下來的回撥函式會放在Event Queue中等待),直到同步任務全部執行完畢,主執行緒就會從Event Queue讀取任務進行執行。該過程會不斷迴圈,即事件迴圈Event Loop。

事件迴圈是如何發生的

不覺得奇怪嗎,如果按照上述同步任務和非同步任務的執行方式,那不是一輪就可以執行完畢嗎,又何來的Event Loop?

這是個小細節,非同步任務存在多個時,每一個非同步任務返回的結果所需的時間都是不同的,這就存在Event Queue以先進先出的形式將返回結果進行排隊,第一個非同步任務返回結果,那麼就將其放在佇列的首位,接下來的非同步任務緊隨其後,就這樣排成一隊。當主執行緒空閒時(即同步任務執行完畢後),便從Event Queue中讀取事件,放入主執行緒執行。而迴圈來自於,當Event Queue執行完畢後,過了一段時間,又有之前的非同步任務返回結果,放到Event Queue中,監控器檢測到Event Queue為非空,主執行緒又開始執行Event Queue中的任務。

巨集任務和微任務

在解釋定義之前,我們先對非同步任務進行說明:

  1. 對伺服器的非同步請求:最常見的非同步任務,這涉及前後端的互動,需要伺服器對請求進行處理,並返回請求結果
  2. setTimeout和setInterval:延時操作,後者為迴圈操作(都涉及延時值)
  3. Promise:JS用來處理非同步操作的物件
  4. process.nextTick(callback):類似node.js版的"setTimeout",在事件迴圈的下一次迴圈中呼叫 callback 回撥函式。

廣義上JS分為同步任務和非同步任務,在此對任務進行更精細的定義:

  • macro-task(巨集任務):包括整體程式碼script,setTimeout,setInterval
  • micro-task(微任務):Promise,process.nextTick

在此,之所以提出巨集任務和微任務,是為了更好的理解事件迴圈!

執行過程:

  • 主執行緒會按順序先執行第一次迴圈的巨集任務,然後將第一次迴圈遇到的微任務放入微任務的Event Queue中,將遇到的巨集任務放入巨集任務Event Queue中,在此特別注意!!第一次迴圈的巨集任務是整體script程式碼!!;
  • 然後後執行微任務的Event Queue;
  • 第二次迴圈時,會從巨集任務的Event Queue中取出第一個巨集任務,然後執行當前巨集任務中包含的程式碼,同樣將遇到的微任務放入微任務的Event Queue中,將遇到的巨集任務放入巨集任務Event Queue中;
  • 再執行當前微任務的Event Queue中的任務;
  • 第三次迴圈,從巨集任務的Event Queue中取出第二個巨集任務…(以此迴圈)

簡而言之,就是先執行巨集任務,再執行微任務,特別注意兩點即可:

  1. 第一次迴圈的巨集任務是整體script程式碼
  2. 巨集任務佇列是一次迴圈執行一條巨集任務

這裡看個例子:

 console.log('1');
 
 setTimeout(function() {
  console.log('2');
  process.nextTick(function() {
   console.log('3');
  })
  new Promise(function(resolve) {
   console.log('4');
   resolve();
  }).then(function() {
   console.log('5')
  })
 })
 process.nextTick(function() {
  console.log('6');
 })
 new Promise(function(resolve) {
  console.log('7');
  resolve();
 }).then(function() {
  console.log('8')
 })
 
 setTimeout(function() {
  console.log('9');
  process.nextTick(function() {
   console.log('10');
  })
  new Promise(function(resolve) {
   console.log('11');
   resolve();
  }).then(function() {
   console.log('12')
  })
 })
 
 //作者:ssssyoki
 //連結:https://juejin.im/post/59e85eebf265da430d571f89
 //來源:掘金

輸出順序為:

1,7,6,8,2,4,3,5,9,11,10,12

總結

在此部落格中,或許包含一些你未曾聽過的名詞或方法,我並未對其進行詳細解釋。之所以如此,是由於,於我個人,在看一些資料時,經常遇到不懂的東西,我會選擇自己進行查閱和理解,這樣更有效於記憶和通透的理解,就跟查單詞是一樣的,如果文本里直接告訴你,反而不會重視。

感興趣的朋友可以使用線上HTML/CSS/JavaScript程式碼執行工具:http://tools.jb51.net/code/HtmlJsRun測試上述程式碼執行效果。

更多關於JavaScript相關內容感興趣的讀者可檢視本站專題:《JavaScript操作DOM技巧總結》、《JavaScript頁面元素操作技巧總結》、《JavaScript事件相關操作與技巧大全》、《JavaScript查詢演算法技巧總結》、《JavaScript資料結構與演算法技巧總結》、《JavaScript遍歷演算法與技巧總結》及《JavaScript錯誤與除錯技巧總結》

希望本文所述對大家JavaScript程式設計有所幫助。