譯文:JS回調(diào)函數(shù)深度指南

我是法醫(yī),一只治療系前端碼猿??,與代碼對話,傾聽它們心底的呼聲,期待著大家的點贊??與關注?,當然也歡迎加入前端獵手技術交流群??,文末掃碼我拉你進群,一起交流技術以及代碼之外的一切???♀?
轉(zhuǎn)載自Duing(ID:duyi-duing)
原文鏈接:https://dev.to/nileshsanyal/javascript-callback-functions-in-depth-guide-for-2019-gj7
作者:Nilesh Sanyal
如有翻譯不準,請多指正。
JS回調(diào)函數(shù)是你必須了解的重要概念,否則,在成為一名成功的前端開發(fā)工程師可能會面臨很多障礙。但我相信,在徹底閱讀本文之后,您將能夠克服以前在回調(diào)方面遇到的任何困難。
本文我將分享關于回調(diào)函數(shù)的很多技巧。但首先,你需要對函數(shù)有一些基本的認識。我的意思是你至少應該知道什么是函數(shù),它是如何工作的,什么是不同類型的函數(shù)等等。
什么是函數(shù)?
函數(shù)是一個邏輯構建塊,其中編寫了一組代碼以執(zhí)行特定任務。實際上,函數(shù)允許以更有條理的方式編寫代碼,這也易于調(diào)試和維護。函數(shù)還允許代碼重用。
函數(shù)只需定義一次,就可以在需要時調(diào)用它,而不必一次又一次地編寫相同的代碼。
我們討論了一下什么是函數(shù),現(xiàn)在,讓我們看看如何在JS中聲明函數(shù)。
使用函數(shù)構造函數(shù):
在此方法中,函數(shù)是在"函數(shù)"構造函數(shù)的幫助下創(chuàng)建的。從技術上講,這種方法不如使用函數(shù)表達式語法和函數(shù)聲明語句語法聲明函數(shù)更高效。

使用函數(shù)表達式:
通常,此方法與變量賦值相同。簡單來說,函數(shù)體被視為表達式,該表達式分配給變量。使用此語法定義的函數(shù)可以是命名函數(shù),也可以是匿名函數(shù)。
沒有名稱的函數(shù)稱為匿名函數(shù)。匿名函數(shù)是自我調(diào)用的,這意味著它會自動調(diào)用自己。此行為也稱為立即調(diào)用的函數(shù)表達式 (IIFE)。


使用函數(shù)聲明語句:
實際上,此方法是JS中常用的老方法。在這里,在關鍵字"函數(shù)"之后,您必須指定函數(shù)的名稱。之后,如果函數(shù)接受多個參數(shù)或參數(shù);你也必須要提到它們。盡管此部分是完全可選的。
在函數(shù)的主體中,函數(shù)必須向調(diào)用方反饋一個值。找到return語句后,該函數(shù)將停止執(zhí)行。在函數(shù)內(nèi)部,參數(shù)將充當局部變量。
此外,在函數(shù)內(nèi)聲明的變量將是該函數(shù)的本地變量。只能在該函數(shù)中訪問局部變量,因此具有相同名稱的變量可以輕松地用于不同的函數(shù)。

發(fā)生以下任一情況時,將調(diào)用之前被聲明的函數(shù):
例如,當事件發(fā)生時,用戶單擊按鈕或用戶從下拉列表中選擇某個選項等。
當從JS代碼調(diào)用函數(shù)時。
該函數(shù)也可以自動調(diào)用,這一點我們已經(jīng)在匿名函數(shù)表達式中討論過了。
()運算符調(diào)用該函數(shù)。
根據(jù)MDN:回調(diào)函數(shù)是作為參數(shù)傳遞給另一個函數(shù)的函數(shù),然后在外部函數(shù)內(nèi)部調(diào)用該函數(shù)以完成某種例程或操作。
我知道在閱讀了這個技術定義之后,你會感到困惑,幾乎無法理解什么是真正的回調(diào)函數(shù)。
讓我用簡單的詞來解釋一下,回調(diào)函數(shù)是一個函數(shù),它將在另一個函數(shù)完成執(zhí)行后立即執(zhí)行。回調(diào)函數(shù)是作為參數(shù)傳遞給另一個JS函數(shù)的函數(shù)。該回調(diào)函數(shù)在它所傳遞到的函數(shù)內(nèi)部執(zhí)行。
在JS中,函數(shù)被視為一等對象。通過說一等對象(我們的意思是一個數(shù)字或一個函數(shù)或一個變量)可以被視為與語言中的任何其他實體相同。作為一個一等對象,我們可以將函數(shù)傳遞給其他函數(shù),因為變量和函數(shù)也可以從其他函數(shù)返回。
可以執(zhí)行此操作的函數(shù)稱為高階函數(shù)。回調(diào)函數(shù)實際上是一種模式。"模式"這個詞意味著某種經(jīng)過驗證的方法來解決軟件開發(fā)中的常見問題。因此,我們最好將回調(diào)函數(shù)的使用稱為回調(diào)模式。
客戶端JS在瀏覽器中運行,主瀏覽器進程是單個線程事件循環(huán)。如果我們嘗試在單線程事件循環(huán)中執(zhí)行長時間運行的操作,則該過程將被阻止。這在技術上是錯誤的,因為該進程在等待操作完成時會停止處理其他事件。
例如,"alert"語句被視為瀏覽器中JS中的阻塞代碼之一。如果您運行警報;您無法再在瀏覽器中執(zhí)行任何交互,直到關閉警報對話框窗口。為了防止阻塞長時間運行的操作,使用回調(diào)。
讓我們深入探討,以便您確切地了解在哪個方案中使用了回調(diào)。

在上面的代碼片段中,getMessage()函數(shù)首先執(zhí)行,然后執(zhí)行displayMessage()。兩者都在瀏覽器的控制臺窗口中顯示一條消息,并且都立即執(zhí)行。
但在某些情況下,某些代碼不會被立即執(zhí)行。例如,如果我們假設getMessage()函數(shù)執(zhí)行API調(diào)用,我們必須將請求發(fā)送到服務器并等待響應,那么我們將如何處理它?
很簡單,為了處理這種情況,我們需要在JS中使用回調(diào)函數(shù)。
我認為與其告訴你JS回調(diào)函數(shù)的語法,嘗試在前面的示例中實現(xiàn)回調(diào)函數(shù)會更好。代碼段顯示在下面的屏幕截圖中。

為了使用回調(diào)函數(shù),我們需要執(zhí)行某種無法立即顯示結(jié)果的任務。為了模擬這種行為,我們使用JS的setTimeout()函數(shù)。該函數(shù)將需要2秒鐘才能在控制臺窗口中顯示消息"Hi,there"。
顯示此消息后,瀏覽器的控制臺窗口中將顯示"Displayedmessage"?。因此,在這種情況下,首先我們正在等待getMessage()函數(shù),在成功執(zhí)行此函數(shù)后,我們將執(zhí)行displayMessage()函數(shù)。
讓我解釋一下在前面的例子中,幕后實際發(fā)生了什么。
從前面的示例中可以看出,在getMessage()函數(shù)中,我們傳遞了兩個參數(shù);第一個參數(shù)是"msg"變量,它顯示在瀏覽器的控制臺窗口中,第二個參數(shù)是"回調(diào)"函數(shù)。
現(xiàn)在,您可能想知道為什么"回調(diào)"函數(shù)作為參數(shù)傳遞。這是因為要實現(xiàn)回調(diào)函數(shù),我們必須將一個函數(shù)作為參數(shù)傳遞給另一個函數(shù)。
在getMessage()函數(shù)完成它的任務后,我們調(diào)用該 "callback()"函數(shù)。之后,當我們調(diào)用getMessage()函數(shù)時,我們傳遞了對"displayMessage()"函數(shù)的引用,該函數(shù)被視為回調(diào)函數(shù)。
請注意,當調(diào)用getMessage()函數(shù)時,我們只是傳遞對 "displayMessage"函數(shù)的引用。這就是為什么,你不會看到函數(shù)調(diào)用運算符,即旁邊的"()"。
JS被認為是一種單線程腳本語言。術語"單線程"意味著JS一次執(zhí)行一個代碼塊。當JS忙于執(zhí)行一個塊時,它不可能移動到下一個塊。
換句話說,我們可以說JS代碼本質(zhì)上總是阻塞的。但是這種阻塞性質(zhì)會妨礙我們在某些情況下編寫代碼,因為我們在運行某些特定任務后無法獲得即時結(jié)果。
我說的是諸如以下之的任務。
向某個終結(jié)點發(fā)送API調(diào)用以獲取數(shù)據(jù)。
發(fā)送網(wǎng)絡請求以從遠程服務器獲取某些資源(例如,文本文件、圖像文件、二進制文件等)。
為了處理這些情況,我們必須編寫異步代碼,回調(diào)函數(shù)是處理這些情況的一種方法。因此,回調(diào)函數(shù)本質(zhì)上是異步的。
當多個異步函數(shù)一個接一個地執(zhí)行時,就會發(fā)生回調(diào)地獄。它也被稱為厄運金字塔。
假設您要獲取所有github用戶的列表,然后在您要搜索的用戶中,僅搜索JS存儲庫的頂級貢獻者。然后,在這些人中,您希望獲得名為Jhon的人的詳細信息。
要在回調(diào)的幫助下實現(xiàn)此功能,代碼片段將如下所示。

從上面的代碼片段中,您可以看到代碼變得更難理解,更難維護,也更難修改。這是由于所有回調(diào)函數(shù)的嵌套而發(fā)生的。
可以使用多種技術來避免回調(diào)地獄,如下所示。
通過使用promise。
在異步的幫助下等待。
通過使用異步.js庫。
我已經(jīng)分享過,如何使用承諾以及異步等待如何有助于避免回調(diào)地獄。
通過使用async.js庫
讓我們來談談使用async.js庫以避免回調(diào)地獄。
根據(jù)async的官方網(wǎng)站.js:Async是一個實用程序模塊,它為使用異步JS提供了直接,強大的功能。
Async.js總共提供了近70個函數(shù)?,F(xiàn)在,我們將只討論其中兩個,即async.waterfall()和async.series()。
async.waterfall()
當您想要一個接一個地運行某些任務,然后將結(jié)果從上一個任務傳遞到下一個任務時,它非常有用。它需要一個函數(shù)"任務"數(shù)組和一個最終的"回調(diào)"函數(shù),該函數(shù)在"任務"數(shù)組中的所有函數(shù)都已完成或使用錯誤對象調(diào)用"回調(diào)"之后調(diào)用。

async.series()
當您想要運行函數(shù),然后在所有函數(shù)成功執(zhí)行后需要獲取結(jié)果時,此函數(shù)非常有用。async.waterfall()和 async.series()之間的主要區(qū)別在于 async.series()不會將數(shù)據(jù)從一個函數(shù)傳遞到另一個函數(shù)。

閉包
用技術術語來說,閉包是將函數(shù)捆綁在一起并引用其周圍狀態(tài)的組合。
簡單地說,閉包允許從內(nèi)部函數(shù)訪問外部函數(shù)的作用域。
要使用閉包,我們需要在另一個函數(shù)中定義一個函數(shù)。然后我們需要返回它或?qū)⑵鋫鬟f給另一個函數(shù)。
回調(diào)
從概念上講,回調(diào)類似于閉包?;卣{(diào)基本上是一個函數(shù)接受另一個函數(shù)作為參數(shù)的地方。
RECOMMEND
? ?
很感謝小伙伴看到最后??,如果您覺得這篇文章有幫助到您的的話不妨關注?+點贊??+收藏??+評論??,您的支持就是我更新的最大動力。
歡迎加入前端獵手技術交流群??,文末掃碼加我微信,我拉你進群,一起交流技術以及代碼之外的一切???♀?
