摘要:如果你把函數的指針地址作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。
同期異步系列文章推薦
談一談javascript異步
javascript異步與promise
javascript異步之Promise.all()、Promise.race()、Promise.finally()
javascript異步之Promise.resolve()、Promise.reject()
javascript異步之Promise then和catch
javascript異步之async(一)
javascript異步之async(二)
javascript異步實戰
javascript異步總結歸檔
我們之前介紹了javascript異步的相關內容,我們知道javascript以同步,單線程的方式執行主線程代碼,將異步內容放入事件隊列中,當主線程內容執行完畢就會立即循環事件隊列,直到事件隊列為空,當用產生用戶交互事件(鼠標點擊,點擊鍵盤,滾動屏幕等待),會將事件插入事件隊列中,然后繼續執行。
處理異步邏輯最常用的方式是什么?沒錯這就是我們今天要說的---回調
如你所知,函數是對象,所以可以存儲在變量中,
所以函數還有以下身份:
可以作為函數的參數
可以在函數中創建
可以在函數中返回
當一個函數a以一個函數作為參數或者以一個函數作為返回值時,那么函數a就是高階函數
回調函數
百度百科
回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。
維基百科
在計算機程序設計中,回調函數,或簡稱回調(Callback 即call then back 被主函數調用運算后會返回主函數),是指通過函數參數傳遞到其它代碼的,某一塊可執行代碼的引用。這一設計允許了底層代碼調用在高層定義的子程序。
回調函數,幾乎每天我們都在用
setTimeout(() => { console.log("這是回調函數"); }, 1000); const hero=["郭靖","黃蓉"] hero.forEach(item=>{ console.log(item); })回調函數解決了哪些問題
舉一個簡單的:
let girlName = "裘千尺" function hr() { girlName = "黃蓉" console.log(`我是${girlName}`); } function gj() { console.log(`${girlName}你好,我是郭靖,認識一下吧`); } hr() gj()
輸出,重點看輸出順序
//=>我是黃蓉 //=>黃蓉你好,我是郭靖,認識一下吧
上面的代碼輸出是沒什么懸念的,不存在異步,都單線程同步執行,最后郭靖和黃蓉相識
如果這時候黃蓉很忙,出現了異步,會怎么樣?
let girlName = "裘千尺" function hr() { setTimeout(() => { girlName = "黃蓉" console.log("我是黃蓉"); }, 0); } function gj() { console.log(`${girlName}你好,我是郭靖,認識一下吧`); } hr() gj()
輸出,重點看輸出順序
//=>裘千尺你好,我是郭靖,認識一下吧 //=>我是黃蓉
雖然定時器是0ms,但是也導致了郭靖和黃蓉的擦肩而過,這不是我們期望的結果,hr函數存在異步,只有等主線程的內容走完,才能走異步函數
所以最簡單的辦法就是使用回調函數解決這種問題,gj函數依賴于hr函數的執行結果,所以我們把gj作為hr的一個回調函數
let girlName = "裘千尺" function hr(callBack) { setTimeout(() => { girlName = "黃蓉" console.log("我是黃蓉"); callBack() }, 0); } function gj() { console.log(`${girlName}你好,我是郭靖,認識一下吧`); } hr(gj)
輸出,重點看輸出順序
//=>我是黃蓉 //=>黃蓉你好,我是郭靖,認識一下吧
??:當回調函數作為參數時,不要帶后面的括號!我們只是傳遞函數的名稱,不是傳遞函數的執行結果
上面小栗子貌似的很簡單,我們繼續
我們把昨天的demo做一下升級
引入了lodash:處理按鈕點擊防抖
axios,集成了promis,但promise不是我們今天討論的內容,我們只使用axios的ajax請求接口功能
easy-mock:接口數據,用來實現ajax請求(數據是假的,但是請求是真的)
javascript回調
仔細看代碼,不難發現,這是一個典型的嵌套回調,我們分析一下
第一層異步,用戶交互,來自按鈕的點擊事件
第二層異步,按鈕去抖,來自lodash下debounce的500ms延時
第三次異步,ajax請求,處理后臺接口數據
拿到數據后我們沒有繼續做處理,在實際工作中可能還存在異步,還會繼續嵌套,會形成一個三角形的縮進區域
再繼續嵌套,就會形成所說的“回調地獄”,就是回調的層級太多了,代碼維護成本會高很多
上面的栗子最多算是入門毀掉地獄,我們看一下這個
function funA(callBack) { console.log("A"); setTimeout(() => { callBack() }, 10); } function funB() { console.log("B"); } function funC(callBack) { console.log("C"); setTimeout(() => { callBack() }, 100); } function funD() { console.log("D"); } function funE() { console.log("E"); } function funF() { console.log("F"); } //從這里開始執行 funA(() => { funB() funC(() => { funD() }) funE() }) funF()
(這段代碼,帶回調的都是異步邏輯)你能很快的看出這段代碼的執行順序嗎?
順序如下:A、F、B、C、E、D
一般正常人不會這么嵌套多層,層級一多,就會考慮拆分
const btn = document.querySelector("button") //監聽按鈕點擊事件 btn.onclick = () => { debounceFun() } //去抖動 const debounceFun = _.debounce(() => { ajax() }, 500) //ajax 請求 const ajax = function () { axios.get("https://easy-mock.com/mock/5b0525349ae34e7a89352191/example/mock") .then(data => { console.log("ajax返回成功"); myData = data.data console.log(myData); }) .catch(error => { console.log("ajax返回失敗"); }) }
我相信很多人都會通過這種鏈式回調的方式處理異步回調,因為可讀性比嵌套回調要搞,但是維護的成本可能要高很多
上面的栗子,三個異步函數之間只有執行順序上的關聯,并沒有數據上的關聯,但是實際開發中的情況要比這個復雜,
我們舉一個簡單的栗子
let girlName = "裘千尺" function hr(callBack) { setTimeout(() => { girlName = "黃蓉" console.log("我是黃蓉"); callBack(girlName) }, 0); } function gj(love) { console.log(`${girlName}你好,我是郭靖,認識一下吧,我喜歡${love}`); } hr(gj)
gj作為hr的回調函數,并且hr將自己的一個變量傳遞給gj,gj在hr的回調中執行,
仔細看這種寫法并不嚴謹,
如果gj并不只是一個function類型會怎么樣?
如果love的實參并不存在會怎么樣?
況且這只是一個簡單的栗子
所以回調函數中,參數的校驗是很有必要的,回調函數鏈拉的越長,校驗的條件就會越多,代碼量就會越多,隨之而來的問題就是可讀性和可維護性就會降低。
但我們引用了第三方的插件或庫的時候,有時候難免要出現異步回調的情況,一個栗子:
xx支付,當用戶發起支付后,我們將自己的一個回調函數,傳遞給xx支付,xx支付比較耗時,執行完之后,理論上它會去執行我們傳遞給他的回調函數,是的理論上是這樣的,我們把回調的執行權交給了第三方,隱患隨之而來
第三方支付,多次調用我們的回調函數怎么辦?
第三方支付,不調用我們的回調函數怎么辦?
當我們把回調函數的執行權交給別人時,我們也要考慮各種場景可能會發生的問題
總結一下:
回調函數簡單方便,但是坑也不少,用的時候需要多注意校驗
原文鏈接
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101162.html
摘要:到這里,我已經發出了一個請求買漢堡,啟動了一次交易。但是做漢堡需要時間,我不能馬上得到這個漢堡,收銀員給我一個收據來代替漢堡。到這里,收據就是一個承諾保證我最后能得到漢堡。 同期異步系列文章推薦談一談javascript異步javascript異步中的回調javascript異步之Promise.all()、Promise.race()、Promise.finally()javascr...
摘要:調用棧被清空,消息隊列中并無任務,線程停止,事件循環結束。不確定的時間點請求返回,將設定好的回調函數放入消息隊列。調用棧執行完畢執行消息隊列任務。請求并發回調函數執行順序無法確定。 異步編程 JavaScript中異步編程問題可以說是基礎中的重點,也是比較難理解的地方。首先要弄懂的是什么叫異步? 我們的代碼在執行的時候是從上到下按順序執行,一段代碼執行了之后才會執行下一段代碼,這種方式...
摘要:異步本質上應該就是多線程語言的產物。如果是多線程的異步,假死的應該是運行方法的線程,而方法仍然會按預期打印出。當然了,按我個人的理解,應該說是是的回調函數。 引子 每個故事都有由來。前兩天在看 gulp 的時候,看到了它有個 promise 的玩意兒,然后的然后,這兩天就掉進了 javascript 的異步和回調的坑里面去了。 其間搜索了 javascript promise,看到了...
摘要:回調函數,一般在同步情境下是最后執行的,而在異步情境下有可能不執行,因為事件沒有被觸發或者條件不滿足。同步方式請求異步同步請求當請求開始發送時,瀏覽器事件線程通知主線程,讓線程發送數據請求,主線程收到 一直以來都知道JavaScript是一門單線程語言,在筆試過程中不斷的遇到一些輸出結果的問題,考量的是對異步編程掌握情況。一般被問到異步的時候腦子里第一反應就是Ajax,setTimse...
摘要:最受歡迎的引擎是,在和中使用,用于,以及所使用的。怎么處理每個引擎都有一個基本組件,稱為調用棧。也就是說,如果有其他函數等待執行,函數是不能離開調用棧的。每個異步函數在被送入調用棧之前必須通過回調隊列。例如方法是在中傳遞的回調函數。 ? 翻譯:瘋狂的技術宅 原文:www.valentinog.com/blog/engine… 從Call Stack,Global Me...
閱讀 1024·2021-10-19 11:42
閱讀 2978·2021-09-10 10:51
閱讀 686·2021-09-09 09:33
閱讀 1766·2021-09-01 10:43
閱讀 2774·2019-08-30 12:43
閱讀 3523·2019-08-30 11:24
閱讀 2128·2019-08-30 10:56
閱讀 2782·2019-08-29 11:00