前端淺談--同步與異步


什么是同步?

   在單線程的情況下,從上往下按順序執行就是同步.

 

什么是異步?

   通俗點解釋很簡單:不是同步的就是異步.也就是說他不是按順序執行的.

   那如何更深刻得去理解.

   1)瀏覽器的渲染進程中JS解析線程就一個,所有的js代碼都是他進行解析.

   2)瀏覽器的渲染進程是有多個子線程的.這個我在<前端淺談-瀏覽器工作進程>里有講到過.這是能造成異步的條件之一.不同步了,就意味着有其他線程參加了.

   3)同步會造成阻塞.當一個任務出現問題之后,下面的所有代碼都會被阻塞掉.這對js來說是致命的.而類似http請求這種任務對於js來說是未知的,我不能冒險讓它影響下面的所有代碼.

   4)異步問題一定是出現在回調函數上

所以,當一個任務的結果對於js來說會造成阻塞或者阻塞的可能性較大時,js解析線程會把它先交給其他線程,最后再去解析和執行回調函數.這一套操作叫做異步.而這些任務被稱為異步任務.

 

宏任務和微任務

  宏任務:宿主環境的函數或者方法 .有script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering

  微任務:js的函數或者方法,有Promises, Object.observe, MutationObserver

關於宿主環境,我的<前端淺談----宿主環境>里面有說過.在異步任務中,也會有先后順序.具體的的順序就要涉及到事件循環.

 

事件循環

  所有的事件會在事件線程中,然后通過事件循環機制進行控制.它的最簡單機制就是,先同再異,先微再宏.

  比如:

        console.log('同步1')
        setTimeout(()=>{
            console.log('setTimeout1')
},0) //宏任務 new Promise((res)=>{
res() console.log('new Promise') //同步 }).then(()=>{ console.log('Promise.then') //微任務 })
console.log('同步2') //同步

  結果

 可以看到,基本就是按照這個順序來執行的.其中有個注意點,這里的new Promise是同步的,但是promise.then()或者catch()這些才是異步.

 以上這個流程只是一次流程,並沒有把循環給體現出來,我們再看看

         console.log('同步1')     //同步     1
        setTimeout(()=>{       //宏任務   
            console.log('setTimeout1')  //宏任務中的同步  5
            new Promise((res)=>{   
res() console.log('new Promise1') //宏任務中的同步 6 }).then(()=>{ //宏任務中的微任務 8 console.log('Promise.then1') }) setTimeout(()=>{ //宏任務中的宏任務 9 console.log('setTimeout2') }) console.log('setTimeout同步') //宏任務中的同步 7 },0) new Promise((res)=>{ //同步 2 console.log('new Promise')
res() }).then(()=>{ //微任務 4 console.log('Promise.then') }) console.log('同步2') //同步 3

  結果:

 所謂的循環就是按照上面的流程遞歸執行,每進入到一個作用域就去執行來一遍,一直到最終把所有的事件執行完成.

 這里還有個疑問,同是宏任務或者微任務,它們的執行順序咋樣?

       setTimeout(()=>{
          console.log('22')
        },1000)
        setInterval(() => {
            console.log('11')
        },1000)

 其結果是22,11.反復嘗試之后得到的結果是.對於同時間的延遲的事件,先寫的先執行,不同時間的時間短的先執行.

 如果是非計時器的函數是怎樣的呢?

首先,事件執行的順序跟推進隊列的順序有關.同作用域的情況下,
process.nextTick有單獨的事件列表,它的優先級最高.然后是微任務表,然后是宏任務列表.再循環.

 

js代碼執行順序(包含事件循環)

  渲染進程包含以下線程

1.GUI線程(主要負責解析HTML、CSS和渲染頁面)

2.JS引擎線程(負責解析和執行JS代碼)

3.事件線程(控制事件循環)

4.定時器線程(處理定時器相關邏輯)

5.異步請求線程(發起Ajax時會生成該線程)

 執行順序

 1. GUI線程優先執行當遇到script標簽之后交給JS引擎線程,而它自己則暫停

 2.同步代碼先執行,遇到定時器就先交給計時器,遇到ajax請求就交給異步請求線程.此時,這兩個線程會完成主線程分配的任務,比如計時器開始計時,ajax請求開始請求.然后計時結束或者ajax請求結束時,會把回調函數注冊進事件循環列表.

 3.當主線程,即JS引擎線程為空時.事件線程會根據事件循環的規則把對應的事件交給JS引擎線程去處理

 4.當JS引擎線程的工作結束時,GUI線程開始接手渲染更新過的頁面

 

異步事件注冊順序和回調函數執行順序不一樣

以下面這段代碼為例

     1   console.log('同步1')                                1
     2   setTimeout(()=>{
            console.log('setTimeout1')                      5
        },0)    //宏任務
     3   new Promise((res)=>{
res() console.log('new Promise') //同步 2 4 }).then(()=>{ console.log('Promise.then') //微任務 4 }) 5 console.log('同步2') //同步 3

  代碼執行順序:左邊的數字(js就是從上往下執行)

  函數注冊進事件循環列表的順序: 1 2 3 4 5 或者  1 3 2 4 5 ,因為這兩個線程誰先結束誰先注冊

  事件循環(回調函數執行順序):右邊的數字

 

 

注意點:1.js是單線程,但瀏覽器渲染進程不是不是.

      2.異步是目的,是為了不等待而且還能拿到對應的數據.而單線程和事件循環是手段.

      3.定時器和異步請求本身是由對應的線程去執行的,但是回調函數會進入事件線程,再由事件線程給JS線程.
4.事件循環是針對回調函數的.

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM