異步無處不在:回調(diào)函數(shù)(二)
(????)??嗨,我是你穩(wěn)定更新、從不斷更的勾勾。

上周五,我們聊了 JS 中的同步模式和異步模式。本篇,我們來嘮嘮回調(diào)函數(shù)。
異步代碼的執(zhí)行流程如下:

通過上圖,我們會(huì)看到,在整個(gè)代碼的執(zhí)行中,JS 本身的執(zhí)行依然是單線程的,異步執(zhí)行的最終結(jié)果,依然需要回到 JS 線程上進(jìn)行處理。
在 JS 中,異步的結(jié)果回到 JS 主線程所采用的是 “ 回調(diào)函數(shù) ” 的形式。
所謂的回調(diào)函數(shù)就是在 JS 主線程上聲明一個(gè)函數(shù),然后將函數(shù)作為參數(shù)傳入異步調(diào)用線程,當(dāng)異步執(zhí)行結(jié)束后,調(diào)用這個(gè)函數(shù),將結(jié)果以實(shí)參的形式傳入函數(shù)的調(diào)用(也有可能不傳參,但是函數(shù)調(diào)用一定會(huì)有)。
上篇的代碼中 setTimeout 就是一個(gè)異步方法,傳入的第一個(gè)參數(shù)就是回調(diào)函數(shù),這個(gè)函數(shù)的執(zhí)行就是消息隊(duì)列中的 “回調(diào)”。
下面我們自己封裝一個(gè) ajax 請求,來進(jìn)一步說明回調(diào)函數(shù)與異步的關(guān)系
Ajax 的異步請求封裝
function myAjax(url,callback) {var xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (this.readyState == 4) {if (this.status == 200) {// 成功的回調(diào)callback(null,this.responseText)} else {// 失敗的回調(diào)callback(new Error(),null);}}}xhr.open('get', url)xhr.send();}
上面的代碼,封裝了一個(gè) myAjax 的函數(shù),用于發(fā)送異步的 ajax 請求。
函數(shù)調(diào)用時(shí),代碼實(shí)際是按照同步模式執(zhí)行的。當(dāng)執(zhí)行到 xhr.send() 時(shí),就會(huì)開啟異步的網(wǎng)絡(luò)請求,向指定的 url 地址發(fā)送。從建立網(wǎng)絡(luò)連接到斷開網(wǎng)絡(luò)連接的整個(gè)過程是異步線程在執(zhí)行的。
換個(gè)說法就是 myAjax 函數(shù)執(zhí)行到 xhr.send() 后,函數(shù)的調(diào)用執(zhí)行就已經(jīng)結(jié)束了。如果 myAjax 函數(shù)調(diào)用的后面有代碼,則會(huì)繼續(xù)執(zhí)行,不會(huì)等待 ajax 的請求結(jié)果。
但是,myAjax 函數(shù)調(diào)用結(jié)束后,ajax 的網(wǎng)絡(luò)請求卻依然在進(jìn)行著。
如果想要獲取到 ajax 網(wǎng)絡(luò)請求的結(jié)果,我們就需要在結(jié)果返回后,調(diào)用一個(gè) JS 線程的函數(shù),將結(jié)果以實(shí)參的形式傳入:
myAjax('./d1.json',function(err,data){console.log(data);})
回調(diào)函數(shù)讓我們輕松處理異步的結(jié)果。但是,如果代碼是異步執(zhí)行的,而邏輯是同步的,就會(huì)出現(xiàn) “回調(diào)地獄”,舉個(gè)栗子:
代碼 B 需要等待代碼 A 執(zhí)行結(jié)束才能執(zhí)行,而代碼 C 又需要等待代碼 B,代碼 D 又需要等待代碼 C,而代碼 A、B、C 都是異步執(zhí)行的。
// 回調(diào)函數(shù) 回調(diào)地獄myAjax('./d1.json',function(err,data){console.log(data);if(!err){myAjax('./d2.json',function(err,data){console.log(data);if(!err){myAjax('./d3.json',function(){console.log(data);})}})}})
沒錯(cuò),代碼執(zhí)行是異步的。但是異步的結(jié)果,是需要有強(qiáng)前后順序的,著名的"回調(diào)地獄"就是這么誕生的。
相對來說,代碼邏輯是固定的,但是編碼體驗(yàn)要差很多,尤其在后期維護(hù)的時(shí)候,層級嵌套太深,讓人頭皮發(fā)麻。
如何讓我們的代碼不在地獄中受苦呢?
下篇告訴你(? ?_?)?。
推薦閱讀:
技術(shù)人年度總結(jié) | 2020,注定不平凡
前端人因?yàn)?Vue3 的 Ref-sugar 提案打起來了!
點(diǎn)點(diǎn)“贊”和“在看”,保護(hù)頭發(fā),減少bug。
