JavaScript之異步編程

最早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)流程:

  1. 宿主環(huán)境(node 服務器或者瀏覽器)為 js 創(chuàng)建線程時,會創(chuàng)建堆(heap)和棧(stack), 堆內存儲 javaScript 對象,棧內存儲執(zhí)行上下文;
  2. 棧內執(zhí)行上下文的同步任務,執(zhí)行完即退棧;當執(zhí)行異步任務時,該異步任務進入等待狀態(tài)(不入棧),同時通知異步進程,執(zhí)行完該異步進程后的回調放到消息隊列中
  3. 當棧內同步任務執(zhí)行結束后,依次執(zhí)行消息隊列中的任務
    注:js是單線程的,瀏覽器不是單線程的,有一些API是有單獨的線程去做的
    image.png

宏任務和微任務

  • 宏任務(macrotask):每次執(zhí)行棧執(zhí)行的代碼就是宏任務(包括每次從消息隊列中獲取一個事件回調并放到執(zhí)行棧中執(zhí)行)
  • 微任務(microtask):當前宏任務執(zhí)行結束后立即執(zhí)行的任務(當前宏任務之后,下一個宏任務之前);
    所以它的響應速度相比 setTimeout 會更快,因為無需等待渲染,也就是說:在某一個宏任務執(zhí)行完后,就會將它執(zhí)行期間產生的所有微任務執(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ù)嵌套很深,就會不可避免的產生回調地獄

// callback就是回調函數(shù)
function foo(callback) {
    setTimeout(function(){
        callback()
    }, 3000)
}

foo(function() {
    console.log('這就是一個回調函數(shù)')
})

Promise —— 一種更優(yōu)的異步編程統(tǒng)一方案

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容