EP 03 | Asynchronous JS 的運用 - Callback Functions Hell

在了解 Callback Functions Hell ,必須先知道甚麼是 Callback Functions ,而 Callback Functions 怎麼造成 Callback Functions Hell

Callback Functions

其實有寫過 JS 的 都應該有用過 Callback Functions(以下簡稱 Callback ),像是常用的 forEach()map()filter()setTimeout() 等等。

什麼是 Callback ?

範例一

1
2
let arr = [1, 2, 3]
arr.forEach(a => a % 2 != 0)

又或者是

範例二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function getMsg(){
console.log('Hello Ajax')
}

function getResponse(cb){
let xhr = new XMLHttpRequest()
xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1')
xhr.send()

xhr.addEventListener('readystatechange', (e)=>{
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr < 300){
cb()
}
}
})
}

getResponse(getMsg)

在 freeCodeCamp 中 What is a Callback Function in JavaScript? 寫到:

A callback function is a function that is passed as an argument to another function, to be “called back” at a later time. A function that accepts other functions as arguments is called a higher-order function.

簡單來說 Callback Function 指的就是做為另一個函數(以下稱函數 A )的引數,並且這個引數在函數 A 中會被調用,而這個接收其他函數作為引數的函數 A 又可以稱做 Higher-Order Function(高階函數)。

所以上面那 2 個 JS 範例, 範例一中 forEach() 帶入的 a => a % 2 != 0 及範例二中 getResponse() 帶入的 getMsg 都是 Callback Function 。

Callback Functions 怎麼變成 Callback Functions Hell 的?

假使透過異步函數得到資料後,要在執行另一個異步函數去取得另一個資料的話,以 Callback Functions 來寫的話會是怎樣?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function getResponse(url, cb){
let xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.send()

xhr.addEventListener('readystatechange', (e)=>{
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
cb(undefined, JSON.parse(xhr.responseText))
}
else{
cb('cann\'t fetch!', undefined)
}
}
})
}

getResponse('https://jsonplaceholder.typicode.com/todos/1', (err, data) => {
console.log(err, data)
// 取得 todos/1 資料後,再去調用 getResponse() 取得另一筆 todos/3 的資料
getResponse('https://jsonplaceholder.typicode.com/todos/3', (err, data) => {
console.log(err, data)
})
})

而 Callback Functions Hell 的形成就是因為太多的 Callback Functions 被一層一層執行下去,導致代碼難以維護。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
getResponse('https://jsonplaceholder.typicode.com/todos/1', (err, data) => {
console.log(err, data)
getResponse('https://jsonplaceholder.typicode.com/todos/3', (err, data) => {
console.log(err, data)
getResponse('https://jsonplaceholder.typicode.com/todos/5', (err, data) => {
console.log(err, data)
getResponse('https://jsonplaceholder.typicode.com/todos/5', (err, data) => {
console.log(err, data)
getResponse('https://jsonplaceholder.typicode.com/todos/5', (err, data) => {
console.log(err, data)
})
})
})
})
})

上面這個範例看起來是不是會覺得很雜亂,想修改也無從下手的感覺,再想想看如果在繼續不斷的在上一層中調用 Callback Function 的話,是不是會覺得很恐怖,這也是為什麼會被稱作 Callback Functions Hell 。

參考資料