1. 程式人生 > 實用技巧 >徹底弄懂 JavaScript 執行機制

徹底弄懂 JavaScript 執行機制

本文的目的就是要保證你徹底弄懂javascript的執行機制,如果讀完本文還不懂,可以揍我

1、關於javascript

javascript是一門單執行緒語言,在最新的HTML5中提出了Web-Worker,但javascript是單執行緒這一核心仍未改變。所以一切javascript版的"多執行緒"都是用單執行緒模擬出來的,一切javascript多執行緒都是紙老虎

2、概念

1. 巨集任務:當前呼叫棧中執行的程式碼成為巨集任務。(主程式碼塊,定時器等等)。

2. 微任務: 當前(此次事件迴圈中)巨集任務執行完,在下一個巨集任務開始之前需要執行的任務,可以理解為回撥事件。(promise.then,proness.nextTick等等)。 3. 巨集任務中的事件放在callback queue中,由事件觸發執行緒維護;微任務的事件放在微任務佇列中,由js引擎執行緒維護。

通俗易懂的例子:
去銀行辦理業務的人就是一個個巨集任務,當巨集任務P1在櫃檯辦理業務時,其它任務都需等待,當一個巨集任務P1辦理業務結束時,櫃檯職員會詢問他還有沒有其它微任務,如果他還有其他業務,則其他巨集任務都需等待。就是微任務是在巨集任務之前執行。

微任務:process.nextTick、MutationObserver、Promise.then catch finally

巨集任務:I/O、setTimeout、setInterval、setImmediate、requestAnimationFrame

巨集任務、微任務的執行順序:

執行順序:先執行同步程式碼,遇到非同步巨集任務則將非同步巨集任務放入巨集任務佇列中,遇到非同步微任務則將非同步微任務放入微任務佇列中,當所有同步程式碼執行完畢後,再將非同步微任務從佇列中調入主執行緒執行,微任務執行完畢後再將非同步巨集任務從佇列中調入主執行緒執行,一直迴圈直至所有任務執行完畢。

3、執行機制

1. 在執行棧中執行一個巨集任務。

2. 執行過程中遇到微任務,將微任務新增到微任務佇列中。

3. 當前巨集任務執行完畢,立即執行微任務佇列中的任務。

4. 當前微任務佇列中的任務執行完畢,檢查渲染,GUI執行緒接管渲染。

5. 渲染完畢後,js執行緒接管,開啟下一次事件迴圈,執行下一次巨集任務(事件佇列中取)。

例項剖析:1

setTimeout(function(){
	console.log('1');
});
new Promise(function(resolve){		    
	console.log('2');
	resolve();
}).then(function(){		    
	console.log('3');
}); 		
console.log('4');
  1. 遇到setTimout,非同步巨集任務,放入巨集任務佇列中;
  2. 遇到new Promise,new Promise在例項化的過程中所執行的程式碼都是同步進行的,所以輸出2;
  3. 而Promise.then中註冊的回撥才是非同步執行的,將其放入微任務佇列中
  4. 遇到同步任務console.log(‘4’);輸出4;主執行緒中同步任務執行完
  5. 從微任務佇列中取出任務到主執行緒中,輸出3,微任務佇列為空
  6. 從巨集任務佇列中取出任務到主執行緒中,輸出1,巨集任務佇列為空,結束~
       控制檯測試一下,輸出2 4 3 1;符合預期

  

例項剖析:2

setTimeout(()=>{
  new Promise(resolve =>{
  	resolve();
  }).then(()=>{
  	console.log('test');
  });

  console.log(4);
});

new Promise(resolve => {
  resolve();
  console.log(1)
}).then( () => {
  console.log(3);
  Promise.resolve().then(() => {
    console.log('before timeout');
  }).then(() => {
    Promise.resolve().then(() => {
      console.log('also before timeout')
    })
  })
})
console.log(2);

  

  1. 遇到setTimeout,非同步巨集任務,將() => {console.log(4)}放入巨集任務佇列中;
  2. 遇到new Promise,new Promise在例項化的過程中所執行的程式碼都是同步進行的,所以輸出1;
  3. 而Promise.then中註冊的回撥才是非同步執行的,將其放入微任務佇列中
  4. 遇到同步任務console.log(2),輸出2;主執行緒中同步任務執行完
  5. 從微任務佇列中取出任務到主執行緒中,輸出3,此微任務中又有微任務,Promise.resolve().then(微任務a).then(微任務b),將其依次放入微任務佇列中;
  6. 從微任務佇列中取出任務a到主執行緒中,輸出 before timeout;
  7. 從微任務佇列中取出任務b到主執行緒中,任務b又註冊了一個微任務c,放入微任務佇列中;
  8. 從微任務佇列中取出任務c到主執行緒中,輸出 also before timeout;微任務佇列為空
  9. 從巨集任務佇列中取出任務到主執行緒,此任務中註冊了一個微任務d,將其放入微任務佇列中,接下來遇到輸出4,巨集任務佇列為空
  10. 從微任務佇列中取出任務d到主執行緒 ,輸出test,微任務佇列為空,結束~
控制檯測試輸出:1 2 3 before timeout also before timeout 4 test

  

例項剖析:3

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')
    })
})


  最後嘗試著做一個比複雜的吧

這是我的分析過程

完整的輸出為1,7,6,8,2,4,3,5,9,11,10,12