Callback
Callback
Callback(回調)是什麼
回調函式是指藉由將函式作為參數(argument)傳遞能通往另一個函式,也就是將控制權轉移到下一個函式中,類似於延續傳遞風格(Continuation-passing style, CPS); 而之對比的是直接風格(Direct style),其在於控制權移交較不明確,他是純粹回傳值,剩下的動作由下一行或其他函式執行。
//直接風格
function getAvatar(user){
return user
}
function display(avatar){
console.log(avatar)
}
const avatar = getAvatar('eddy')
display(avatar)
//CPS
function getAvatar(user, cb){
cb(user)
}
function display(avatar){
console.log(avatar)
}
getAvatar('eddy', display)
CPS優點
- 程式碼精簡
- 可搭配非同步函式來達到非阻塞式I/O
- 可以提高函式的擴充性與重覆使用性
缺點
- 在愈複雜的應用情況時,程式碼愈不易撰寫與組織,維護性與閱讀性較低
- 在錯誤處理上較為困難
非同步回調函式
在前面的範例是以同步(Synchronous)的方式來執行callback,但大部分的callback都是用在非同步(Asynchronous)執行的,來達到不阻塞其他程式的進行。除了小部分的語言的API中使用的回調可能會使用同步方式之外,其他大部分都是使用非同步回調函式,如下幾種:
- DOM事件
- Ajax
- 使用計時器(timer)函式:
setTimeout
,setInterval
- 特殊的函式:
nextTick
,setImmediate
- 執行I/O: 監聽網路、資料庫查詢或讀寫外部資源
- 訂閱事件
非同步回調函式範例
function aFunc(value, callback){
callback(value)
}
function bFunc(value, callback){
setTimeout(callback, 0, value)
}
function cb1(value){ console.log(value) }
function cb2(value){ console.log(value) }
function cb3(value){ console.log(value) }
function cb4(value){ console.log(value) }
aFunc(1, cb1)
bFunc(2, cb2)
aFunc(3, cb3)
bFunc(4, cb4)
1
3
2
4
回調函式的寫法
明確定義回調函式名稱
function func(x, cb){
cb(x)
}
function callback(value){
console.log(value)
}
func(123456, callback)
使用匿名(anonymous)函式
function func(x, cb){
cb(x)
}
func(123456, function(value){
console.log(value)
})
- callback函式名稱可以用匿名函式取代
- callback函式傳入的
value
名稱可以自由定義 - callback函式有Closure(閉包)結構的特性,可以獲取到
func
中的傳入參數,以及裡面的定義的值
回調地獄(Callback Hell)
當程式開發的越來越複雜時,可能會開始一個接著一個執行callback流程,進而形成了一個回調地獄(Callback Hell),使得程式較難維護、可讀性變差且難以追蹤問題,如下所示。
function getRecipe(){
setTimeout(()=>{
const recipeID = [123, 839, 520, 752];
console.log(recipeID);
setTimeout(id => {
const recipe = {title: 'Fresh tomato pasta',
publisher: 'Jay'};
console.log(`${id}: ${recipe.title}`);
setTimeout(publisher => {
const recipe2 = {title: 'Italian Pizza',
publisher: publisher};
console.log(recipe2);
}, 1500, recipe.publisher);
}, 1500, recipeID[2]);
}, 1500);
}
getRecipe();
[123, 839, 520, 752]
“520: Fresh tomato pasta”
[object Object] { publisher: “Jay”, title: “Italian Pizza” }
Ref
Author Yujay
LastMod 2018-07-01