單線程
- 瀏覽器是多線程運行的,它給js分配一個線程;js就是單線程運行的【一次只干一件事】
所謂單線程,就是只一次只能完成一件任務。如果有多個任務,就必須排隊,前面一個任務完成,在執行后面一個任務,以次類推。
js執行分為同步和異步,其中異步來自於瀏覽器提供的異步隊列,在瀏覽器中分為兩個任務隊列,一個是主任務隊列【同步編程】,一個是等待任務隊列【異步編程】
了解js的異步我們應該先了解下js的運行環境=>瀏覽器
一個瀏覽器通常由一下幾個常住的線程
- 渲染引擎線程:顧名思義,該線程負責頁面的渲染
- js引擎線程:負責js解析和執行
- 定時觸發器線程:處理定時事件,比如
setTimeout
,setInterval
- 事件觸發線程:處理DOM事件
- 異步http請求線程:處理http請求
注意:渲染線程和js引擎線程是不能同時進行的。渲染線程在執行任務的時候,js引擎線程會被掛起。因為js可以操作DOM,若在渲染中js處理了DOM,瀏覽器可能就不知所措了
雖然JavaScript是單線程的,可是瀏覽器內部不是單線程的。一些I/O操作、定時器的計時和事件監聽(click, keydown...)等都是由瀏覽器提供的其他線程來完成的。
Javascript語言將任務的執行模式分成兩種:同步(Synchronous)和異步(Asynchronous)。
"同步模式"就是上一段的模式,后一個任務等待前一個任務結束,然后再執行,程序的執行順序與任務的排列順序是一致的、同步的;"異步模式"則完全不同,每一個任務有一個或多個回調函數(callback),前一個任務結束后,不是執行后一個任務,而是執行回調函數,后一個任務則是不等前一個任務結束就執行,所以程序的執行順序與任務的排列順序是不一致的、異步的。
"異步模式"非常重要。在瀏覽器端,耗時很長的操作都應該異步執行,避免瀏覽器失去響應,最好的例子就是Ajax操作。在服務器端,"異步模式"甚至是唯一的模式,因為執行環境是單線程的,如果允許同步執行所有http請求,服務器性能會急劇下降,很快就會失去響應。
同步
console.log(1)
function fn(){
console.log(2)
}
fn()
console.log(3)
上面代碼輸入1 2 3 ,因為js是單線程的,代碼由上而下依次執行
異步
js中
- AJax請求
- 定時器
- 事件
- 回調函數
上面這4中都是屬於異步的
console.log(1)
setTimeout(()=>{
console.log(2)
})
console.log(3)
上面代碼執行會輸出,1 3 2 ,因為
setTimeout
是異步的,js會將setTimeout
放到異步隊列,等待同步隊列全部執行完畢,在執行異步隊列
任務隊列
js中有兩類任務隊列:宏任務隊列和微任務隊列。宏任務隊列可以有多個,微任務隊列只有一個
- 宏任務:script(全局任務),setTimeout,setInterval
- 微任務:process.nextTick, Promise, Object.observer
setTimeout(_ => console.log(4))
new Promise(resolve => {
resolve()
console.log(1)
}).then(_ => {
console.log(3)
})
console.log(2)
上面代碼中,setTimeout就是作為宏任務來存在的,而Promise.then則是具有代表性的微任務
所有會進入的異步都是指的事件回調中的那部分代碼
也就是說
new Promise
在實例化的過程中所執行的代碼都是同步進行的,而then
中注冊的回調才是異步執行的。
在同步代碼執行完成后才回去檢查是否有異步任務完成,並執行對應的回調,而微任務又會在宏任務之前執行。所以就得到了上面的輸出1 2 3 4