Javascript非同步機制

聶雄發表於2018-06-18

Javascript作為一種單執行緒語言,是如何實現非同步程式設計的呢?

相信不少人對Javascript單執行緒表示懷疑:為何單執行緒可以實現非同步操作呢?其實Javascript確實是單執行緒的(我們不妨把這個執行緒稱作主執行緒),但它實現非同步操作的方式確實藉助了瀏覽器的其他執行緒的幫助。那其他執行緒是怎麼幫助Javascript主執行緒來實現非同步的呢?答案就是任務佇列(task queue)和事件迴圈(event loop)。

一、任務佇列

首先,作為單執行緒語言,在Javascript中定義的任務都會在主執行緒中執行。但是並不是每個任務都會立刻執行,而這種不立刻執行的任務我們稱作非同步任務。相反,那些立刻執行的任務我們把它們稱作同步任務。而這些非同步任務都會交給瀏覽器的其他執行緒去執行,但是主執行緒需要了解這些非同步任務執行的狀態,才方便進行下一步操作。

打個比方,主執行緒準備做飯,所以下達一個非同步任務去買菜,非同步任務買完菜之後得告訴主執行緒:“我買完菜啦”,這個時候主執行緒才好開始做飯。

而我們知道因為Javascript是單執行緒,所以上述的“下一步操作”沒法直接定義在主函式裡(不然就被當做同步任務直接執行了),那這些應該定義在哪裡呢?答案就是非同步任務的回撥函式中。在Javascript非同步機制中,任務佇列就是用來維護非同步任務回撥函式的佇列。這樣一個佇列用來存放這些回撥函式,它們會等到主執行緒執行完所有的同步函式之後按照先進先出的方式挨個執行。那麼執行完任務佇列之後呢?Javascript主執行緒就執行完畢了嗎?當然不是,不然網頁載入完畢之後,誰來處理後續與使用者的互動事件(比如點選事件)呢?

二、事件迴圈

javascript_asyc.jpg

我們通過上圖來更加形象的瞭解Javascript的非同步機制。
執行同步任務 -> 檢查任務佇列中是否有任務 -> [有如果則執行] -> 檢查任務佇列中是否有任務 -> [有如果則執行] -> ……
可見主執行緒在執行完同步任務之後,會無限迴圈地去檢查任務佇列中是否有新的“任務”,如果有則執行。而這些任務包括我們在非同步任務中定義的回撥函式,也包括使用者互動事件的回撥函式。通過事件迴圈,Javascript不僅很好的處理了非同步任務,也很好的完成了與使用者互動事件的處理。因為在完成非同步任務的回撥函式之後,任務佇列中的任務都是由事件所產生的,因此我們也把上述的迴圈過程叫做事件迴圈

三、非同步機制實踐

console.log(`定時器去買菜吧`)
setTimeout(function(){
    console.log(`菜買完了,主執行緒去做菜吧`)
}, 0)
console.log(`你先去買菜,我先看個世界盃`)

在瀏覽器中執行上述程式碼,興許能更好地理解Javascript的非同步機制。

四、總結

總而言之,Javascript單執行緒的背後有瀏覽器的其他執行緒為其完成非同步服務,這些非同步任務為了和主執行緒通訊,通過將回撥函式推入到任務佇列等待執行。主執行緒所做的就是執行完同步任務後,通過事件迴圈,不斷地檢查並執行任務佇列中回撥函式。


相關文章