最早js語言就是運行在瀏覽器端的語言吐句,目的是為了實現(xiàn)頁面上的動態(tài)交互牺勾。實現(xiàn)頁面交互的核心就是DOM操作,這就決定了它必須使用單線程模型伐债,否則就會出現(xiàn)很復雜的線程同步問題。 假設在js中有多個線程一起工作致开,其中一個線程修改了這個DOM元素峰锁,同時另一個線程又刪除了這個元素,此時瀏覽器就無法明確該以哪個工作線程為準喇喉。所以為了避免線程同步的問題祖今,從一開始,js就設計成了單線程的工作模式拣技。
單線程優(yōu)缺點:
單線程優(yōu)點就是更安全千诬,簡單;缺點就是如果碰到很耗時的任務(比如ajax請求膏斤,文件讀寫)徐绑,會出現(xiàn)假死情況,用戶體驗差莫辨,所以就出現(xiàn)了同步任務和異步任務來解決這個問題傲茄;
同步模式和異步模式
- 同步模式:代碼按順序一行一行執(zhí)行,是典型的請求-相應模式沮榜,執(zhí)行順序和編寫順序保持一致盘榨;
console.log('global begin')
function bar () {
console.log('bar task')
}
function foo () {
console.log('foo task')
bar()
}
foo()
console.log('global end')
// global begin
// foo task
// bar task
//global end
// 使用調用棧的邏輯
- 異步模式:任務可以同時執(zhí)行,不必等待上一個任務結束才繼續(xù)執(zhí)行蟆融;(比如生活中草巡,你可以同時燒水和煮飯一樣)
console.log('global begin')
// 延時器
setTimeout(function timer1 () {
console.log('timer1 invoke')
}, 1800)
// 延時器中又嵌套了一個延時器
setTimeout(function timer2 () {
console.log('timer2 invoke')
setTimeout(function inner () {
console.log('inner invoke')
}, 1000)
}, 1000)
console.log('global end')
// global begin
// global end
// timer2 invoke
// timer1 invoke
// inner invoke
//除了調用棧,還用到了消息隊列和事件循環(huán)
js 執(zhí)行異步代碼而不用等待型酥,是因有為有 消息隊列和事件循環(huán)山憨。
- 消息隊列:消息隊列是一個先進先出的隊列,它里面存放著各種消息弥喉。
- 事件循環(huán)(EventLoop):事件循環(huán)是指主線程重復從消息隊列中取消息郁竟、執(zhí)行的過程
事件循環(huán)流程:
- 宿主環(huán)境(node 服務器或者瀏覽器)為 js 創(chuàng)建線程時,會創(chuàng)建堆(heap)和棧(stack)由境, 堆內存儲 javaScript 對象棚亩,棧內存儲執(zhí)行上下文;
- 棧內執(zhí)行上下文的同步任務虏杰,執(zhí)行完即退棧讥蟆;當執(zhí)行異步任務時,該異步任務進入等待狀態(tài)(不入棧),同時通知異步進程嘹屯,執(zhí)行完該異步進程后的回調放到消息隊列中
- 當棧內同步任務執(zhí)行結束后攻询,依次執(zhí)行消息隊列中的任務
注:js是單線程的,瀏覽器不是單線程的州弟,有一些API是有單獨的線程去做的
image.png
宏任務和微任務
- 宏任務(macrotask):每次執(zhí)行棧執(zhí)行的代碼就是宏任務(包括每次從消息隊列中獲取一個事件回調并放到執(zhí)行棧中執(zhí)行)
-
微任務(microtask):當前宏任務執(zhí)行結束后立即執(zhí)行的任務(當前宏任務之后钧栖,下一個宏任務之前);
所以它的響應速度相比 setTimeout 會更快婆翔,因為無需等待渲染拯杠,也就是說:在某一個宏任務執(zhí)行完后,就會將它執(zhí)行期間產(chǎn)生的所有微任務執(zhí)行完畢啃奴;
舉個例子:
我去銀行排隊辦理業(yè)務潭陪,原本我只想辦理取款業(yè)務(取款業(yè)務當成是宏任務),辦理完取款業(yè)務后,立即我又想辦一個開卡業(yè)務(開卡業(yè)務當成一個微任務);
這個時候依溯,我不會重新去排隊老厌,而是在還沒離開辦理窗口時,立馬讓柜臺人員幫我再次辦理這個業(yè)務黎炉;
如果我還有其他業(yè)務要辦理(更多微任務),都是可以繼續(xù)辦理枝秤,只要我不離開窗口(后面排隊用戶也不應該有任何怨言,因為我有排隊并且沒有離開柜臺)慷嗜。
宏任務包含:
script(整體代碼)
setTimeout
setInterval
I/O
UI交互事件
postMessage
MessageChannel
setImmediate(Node.js 環(huán)境)
微任務包含:
Promise.then
Object.observe
MutaionObserver
process.nextTick(Node.js 環(huán)境)
回調函數(shù)(異步編程的根基)
概念:由調用者定義淀弹,交給執(zhí)行者執(zhí)行的函數(shù)
缺點:如果異步函數(shù)嵌套很深,就會不可避免的產(chǎn)生回調地獄
// callback就是回調函數(shù)
function foo(callback) {
setTimeout(function(){
callback()
}, 3000)
}
foo(function() {
console.log('這就是一個回調函數(shù)')
})