回呼?回調?
Callback Function 到底 Call 了什麼東西回去?
就讓我們來瞭解回呼函式到底在做什麼吧!
callback function
回呼函式(Callback Function)其實就是函式,只是一般都會定義某個函式為另一個函式的參數,當透過另一個函式呼叫該函式時,此時我們就可以說它是回呼函式。
比如我們從 isbndb.com
找到了一隻可以查閱書籍相關內容的 API,我們可以將其寫成下列函式:
1 | function getBookInfo(ISBN) { |
但如果我們想要取得書籍資料後做一些處理的話,直接寫在函式裡面就會難以重複利用,並且會副作用在裡面:
1 | function getBookInfo(ISBN) { |
這時候我們可以將要處理的函式作為參數傳進去,使得取得資料的時候去呼叫這隻函式,接著就可以在使用函式的同時,同時定義好取到資料後要做什麼事情:
1 | function getBookInfo(ISBN, cb) { |
從上面範例中可以看見,回呼函式輕鬆的將耦合降低了,我們將來要使用不同的 ISBN 取得書目資料時,我們不必再去更改 getBookInfo
函式裡的內容,而是在使用函式時可以自由的定義取到資料後要進行的處理!
error first
在上方的例子中我們可以看見我們只有定義了取得資料後的狀態,並沒有定義取得資料失敗的狀態,因此我們要在把錯誤狀態加進我們的回呼函式:
1 | // 先不管 getBookInfo 內部的實作 |
從上面例子可以看到當我們在執行 getBookInfo
函式的回呼函式時,我們並無法知道 x
、y
傳進來的參數代表什麼意思,此時如果要你選一個參數當判斷取得資料成功與否你會選擇哪個?
較好的做法其實是使用第一個參數來做判斷:
1 | function getBookInfo(ISBN, cb) { |
為什麼會是第一個參數呢?原因在於我們對於成功取得資料的與否這件事情是一定會遇到的,所以我們可以藉由放在第一個參數來減低不必要的參數使用。
1 | getBookInfo('9862764414', function(a, b, c, d, err){ |
上面例子則是假如 callback function 第五個才是 err 處理,可以看見儘管我們只有要使用 a
參數與 err
處理,我們仍然要把其他參數列好列滿。
callback hell
當我們以為終於完成 Callback function 時,上忍告訴你事情沒有這麼簡單就結束,假如今天有一個狀況是有一連串的函式需要按順序執行時,你第一個可能會想到這麼做:
1 | function funcA() { |
但你現在已經會回呼函式了,因此改寫成:
1 | function funcA(cb) { |
你不禁讚嘆自己如同 JavaScript 忍者一樣寫出低耦合的程式,如同一位上忍般解決了一個艱困的難題
直到當我們有多個順序需要執行時:
1 | funcA( |
「此時你就可以看到著名的回呼地獄(Callback Hell),而且這還不含錯誤處理呢。」上忍如是說,「至於要怎麼解決,只能詳閱 Promise 一文」。