2、事件輪巡

一、JS為何是單線程的?
JavaScript語言的一大特點就是單線程,也就是說,同一個時間只能做一件事。那么,為什么JavaScript不能有多個線程呢?這樣能提高效率啊。(在JAVA和c#中的異步均是通過多線程實現(xiàn)的,沒有循環(huán)隊列一說,直接在子線程中完成相關(guān)的操作)

 JavaScript的單線程,與它的用途有關(guān)。作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM。這決定了它只能是單線程,否則會帶來很復雜的同步問題。比如,假定JavaScript同時有兩個線程,一個線程在某個DOM節(jié)點上添加內(nèi)容,另一個線程刪除了這個節(jié)點,這時瀏覽器應該以哪個線程為準?

所以,為了避免復雜性,從一誕生,JavaScript就是單線程,這已經(jīng)成了這門語言的核心特征,將來也不會改變。

為了利用多核CPU的計算能力,HTML5提出Web Worker標準,允許JavaScript腳本創(chuàng)建多個線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個新標準并沒有改變JavaScript單線程的本質(zhì)

二、JS是單線程的,那么他是如何是實現(xiàn)異步操作的?

JS的異步是通過回調(diào)函數(shù)實現(xiàn)的,即通過任務隊列,在主線程執(zhí)行完當前的任務棧(所有的同步操作),主線程空閑后輪詢?nèi)蝿贞犃?,并將任務隊列中的任務(回調(diào)函數(shù))取出來執(zhí)行。"回調(diào)函數(shù)"(callback),就是那些會被主線程掛起來的代碼。異步任務必須指定回調(diào)函數(shù),當主線程開始執(zhí)行異步任務,就是執(zhí)行對應的回調(diào)函數(shù)。

雖然JS是單線程的但是瀏覽器的內(nèi)核是多線程的,在瀏覽器的內(nèi)核中不同的異步操作由不同的瀏覽器內(nèi)核模塊調(diào)度執(zhí)行,異步操作會將相關(guān)回調(diào)添加到任務隊列中。而不同的異步操作添加到任務隊列的時機也不同,如 onclick, setTimeout, ajax 處理的方式都不同,這些異步操作是由瀏覽器內(nèi)核的 webcore 來執(zhí)行的,webcore 包含上圖中的3種 webAPI,分別是 DOM Binding、network、timer模塊。
 // onclick 由瀏覽器內(nèi)核的 DOM Binding 模塊來處理,當事件觸發(fā)的時候,回調(diào)函數(shù)會立即添加到任務隊列中。

 // setTimeout 會由瀏覽器內(nèi)核的 timer 模塊來進行延時處理,當時間到達的時候,才會將回調(diào)函數(shù)添加到任務隊列中。

 // ajax 則會由瀏覽器內(nèi)核的 network 模塊來處理,在網(wǎng)絡(luò)請求完成返回之后,才將回調(diào)添加到任務隊列中。

JS中的異步運行機制如下:

(1)所有同步任務都在主線程上執(zhí)行,形成一個執(zhí)行棧(execution context stack)。
(2)主線程之外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結(jié)果,就在"任務隊列"之中放置一個事件。
(3)一旦"執(zhí)行棧"中的所有同步任務執(zhí)行完畢,系統(tǒng)就會讀取"任務隊列",看看里面有哪些事件。那些對應的異步任務,于是結(jié)束等待狀態(tài),進入執(zhí)行棧,開始執(zhí)行。
(4)主線程不斷重復上面的第三步。

只要主線程空了,就會去讀取"任務隊列",這就是JavaScript的運行機制。這個過程會不斷重復。(該過程又稱之為事件輪詢)
三、JS種事件隊列的優(yōu)先級

在JS中ES6 中新增的任務隊列(promise)是在事件循環(huán)之上的,事件循環(huán)每次 tick 后會查看 ES6 的任務隊列中是否有任務要執(zhí)行,也就是 ES6 的任務隊列比事件循環(huán)中的任務(事件)隊列優(yōu)先級更高。

如 Promise 就使用了 ES6 的任務隊列特性。也即在執(zhí)行完任務棧后首先執(zhí)行的是任務隊列中的promise任務。其他的上面常見的異步操作加入隊列的時間沒有相應的優(yōu)先級。
一個事件循環(huán)是可以有多個任務隊列的,每個任務都有一個任務源,相同任務源的任務只能在一個任務隊列中,一個任務隊列的執(zhí)行順序是先進先出,多個任務隊列有優(yōu)先級,但根據(jù)宿主環(huán)境的不同,優(yōu)先級也不同,不能保證固定。

es6標準中,任務又分為兩種類型,宏任務(macrotask)和微任務(microtask)。

宏任務:由宿主環(huán)境提供,比如setTimeout、setInterval、網(wǎng)絡(luò)請求、用戶I/O、script(整體代碼)、UI rendering、setImmediate(node)。
微任務:語言標準(ECMAScript)提供,如process.nextTick(node)、Promise.then()、Object.observe、MutationObserver。

console.log(1)
setTimeout(function () {
    console.log(6)
}, 0)
new Promise(function (resolve, reject) {
    console.log(2)
    setTimeout(function () {
        console.log(3)
    }, 0)
    resolve()
}).then(function (res) {
    console.log(4)
})
console.log(5)
setTimeout(function () {
    console.log(7)
}, 0)
new Promise(function (resolve, reject) {
    console.log(8)
    setTimeout(function () {
        console.log(9)
        new Promise(function (resolve, reject) {
            console.log(11)
            setTimeout(function () {
                  console.log(12)
            }, 0)
          resolve()
        }).then(function (res) {
                console.log(13)
          })
     }, 0)
    resolve()
}).then(function (res) {
    console.log(10)
})
//1  2  5  8  4 10  6  3  7  9 11 13  12
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容