回呼函數
回呼函數(英文:callback function,本來淨叫 callback)係泛指任何佢嘅參照會傳嚟傳去嚟用嘅子程式[1][註 1]。實際啲講,就即係用某種方法指定,喺某啲情況下(例如某特定嘅事件發生咗,或者符合咗某條件嗰陣)執行嘅子程式;指定嘅方法包括將佢擺喺張表入面、將佢當做程式參數咁用[1]、擺佢喺個變數度等等。
要用回呼函數,隻程式語言一定要有方法以資料類型表達函數本身或者函數嘅參照,前者可以係碼塊或者閉包(closure),而後者就可以係位址、指標(function pointer)或者識別碼(即係子程式個名);如果喺某隻程式語言入面冇辦法表示回呼函數嘅參照,嗰隻程式語言會用唔到回呼函數。
回呼函數可以用嚟處理非同步操作,例如用嚟處理(唔知幾時發生,或者唔知會唔會發生嘅)事件、定時器或資料請求完成後執行某啲操作。
概念上,回呼函數係抽象化一種,將未知嘅動作抽象化,變成參數或者變數,可以令到程式碼更具有彈性同可重用性。呢種設計模式可以令到開發人員將程式邏輯同具體嘅操作分開,並喺需要時調用回呼函數以執行特定嘅任務或操作。
例子
編輯決定型執行
編輯首先嚟個簡單嘅例子,唔涉及任何非同步操作。喺呢個 Perl 嘅例入面有兩個子程式預咗用嚟做回呼函數,叫 a 同 b;另外一個子程式 g 有兩個參數,參數一係指定嘅函數(亦即係回呼函數),參數二係一個數值。主程式第一次用回呼函數 a,第二次用回呼函數 b,第一次會輸出 g(a, 20) = a(20, 10) = 20 + 10 = 30,第二次輸出 g(b, 20) = b(20, 10) = 20 - 10 = 10。
use feature 'signatures';
no warnings 'experimental::signatures';
# 回呼函數之一
sub a ($x, $y) {
return $x + $y;
}
# 回呼函數之二
sub b ($x, $y) {
return $x - $y;
}
# 用回呼函數嘅子程式
sub g ($f, $x) {
return &$f($x, 10);
}
# 主程式
printf "%d\n", g(\&a, 20);
printf "%d\n", g(\&b, 20);
子程式 g 唔知指定嘅回呼函數其實會做乜,但係假定佢有一個參數,亦假定佢會輸出一個數值。喺呢個例入面,指定回呼函數嘅參數係現成函數嘅參照。
呢個例唔一定要咁寫;喺 Perl,回呼函數可以係閉包,亦可以係識別碼,不過用識別碼嘅做法喺目前並唔鼓勵。
非同步執行
編輯喺呢個 Javascript 嘅例入面,主程式呼叫一個叫 doSomethingLater 嘅子程式,參數係以閉包形式封存嘅回呼函數。doSomethingLater 呼叫標準函數 setTimeout 嚟設定一個 1000 毫秒(即係一秒)嘅定時器,第一個參數係另一個以閉包形式封存嘅回呼函數,後者呼叫以特定參數 「Done」 呼叫主程式畀佢嘅回呼函數。整體上,「Done」 呢個訊息會喺主程式開始執行嘅一秒後輸出。
// 回呼函數作為參數傳遞畀另一個函數
function doSomethingLater(whatToDo) {
setTimeout(function() {
whatToDo("Done");
}, 1000);
}
// 呼叫包含回呼函數嘅函數
doSomethingLater(function(result) {
console.log(result); // 輸出: "Done",喺1秒後執行
});
技術上,喺呢個例入面嘅兩個回呼函數都係閉包本身,因為閉包喺定義上就即係冇名嘅函數,所以呢個例子比較複雜,因為涉及到兩個唔同嘅回呼函數;主程式畀嘅回呼函數喺子程式用嚟畀 setTimeout 用,係決定型執行,但係 setTimeout 嘅第一個參數亦係回呼函數,係非同步執行。
概念上,呢個例示範嘅係將 doSomethingLater 要做嘅動作抽象化;動作會喺內定(hard code)咗嘅時間後做,但係要做乜嘢動作,就喺參數指定。主程式同 doSomethingLater 之間有個默契,就係回呼函數會接受一個參數。
優點
編輯- 非同步操作:回呼函數通常用喺處理非同步操作,例如事件處理、資料請求或定時器等。噉樣可以避免程式阻塞,提高應用程式嘅效能同反應速度。
- 模組化同可重用性:使用回呼函數可以將程式碼模組化,令到程式具有更好嘅結構性同可重用性。開發人員可以定義一次性嘅回呼函數,然後喺需要時重複使用。
- 事件處理:喺事件驅動嘅程式設計中,回呼函數可以用喺處理唔同嘅事件,令到程式碼更具靈活性同可擴展性。
- 參數靈活性:回呼函數可以接受唔同嘅參數,令到函數更具通用性同彈性。噉樣可以令到函數更容易擴展同適應唔同嘅情況。
缺點
編輯- 回呼地獄(callback hell):當回呼函數嵌套過深或過多時,可能導致程式碼可讀性下降,出現所謂嘅「回呼地獄」問題,令到程式難以維護同理解。
- 錯誤處理困難:回呼函數嘅錯誤處理可能較為困難,特別係當多個回呼函數鏈接喺一齊時,可能難以追蹤同處理錯誤。
- 執行順序難以預測:當使用多個回呼函數時,執行嘅順序可能難以預測,尤其是係處理非同步操作時,可能會出現意外嘅執行順序。
- 可讀性同維護性:過多嘅回呼函數可能導致程式碼複雜度增加,降低程式碼嘅可讀性同維護性,令到代碼難以管理同修改。
出面網頁
編輯註釋
編輯參考資料
編輯- ↑ 1.0 1.1 Srinivasan, Sriram (2005). "Using Subroutine References". Advanced Perl Programming (英文). O’Reilly. 喺2024年10月2號搵到.