第二章 回撥函式

王工發表於2016-05-06

上一章瞭解了基本的術語,我們知道了在分塊(函式)中,程式碼順序是不會被打亂的,但是在分塊之上,就無法確定誰先誰後了。

無論一個 JS 程式碼寫的多複雜,回撥函式是JS非同步程式設計的基礎,如果 JS 的非同步沒有缺點該多好,多少開發者將為這個承諾(雙關 Promise)歡呼,然而就算你知道 promise 是對回撥函式的抽象,你要先知道為什麼要抽象,才能真正理解後面的東西。

本章的主要內容就是告訴你,為什麼我們需要更好的非同步模式。

延續 (Continuations)

回撥(callback)函式就是對未來要執行的事件進行封裝。關鍵是要理解回撥函式表達與管理時出現的各種難以應對的情況

線性的大腦

你可以一邊說話一邊打字,但你只不過是在兩種任務間來回切換而已,這和JS的執差不多。然而問題出在了程式碼書寫上,你不能用線性的大腦去線性的讀 JS 非同步程式碼,這是回撥函式的最大缺點。

巢狀回撥

你想讓回撥程式碼讓人看的清晰,就會出現 回撥地獄 ,也就是常說的 惡魔金字塔 。有時候你還要判斷一個函式做為引數也可能不是回撥。實際工作中JS程式碼的混亂程度有過之而無不及。這還不是最要命的,要命的是我們之前說過的那些競爭條件。

信任問題

你開發了的一個程式,裡面有個回撥函式,會獲取定單金額,過去半年,突然要改,要你加入一個門閂,因為可能要從多個位置獲取資料,然後過幾天QA又要你寫一個回撥沒有返回的錯誤處理等等,你不得不加入許多後驗機制防止各種毛病。這樣你要為每一個回撥函式裡新增許多程式碼。

別人的程式碼

你可以寫程式碼,別人也寫, 你們互相使用,但是如何使用才能不出意外,也許你要讓一個函式接受 數字作引數,別人就傳入字串會出錯,這時你你需要在編寫時就遵循“信任但驗證”的原則。回撥函式也應該這樣。

拯救回撥

為了解決後驗問題,出現了兩種回撥設計模式 ,一種是分離設計(Promise),另一種是錯誤優先處理(node.js)。

但是這並沒有解決根本問題?比如你要取消回撥,一個回撥函式是不是太早被呼叫了,還有就是分不清到底一個函式作引數是不是回撥(Zalgo怪獸,因此要遵循傳入的函式都是回撥的設計原則)等等問題。

傳入的到底是不是非同步函式呢?

var a = 0;

show(sync);  //=> 0
show(Async); // 1秒後 => 1

a++;

function show(arg) {
    arg();
}

function sync() {
    console.log(a);
}

function Async() {
    setTimeout(function(){
        console.log(a)
    },1000)
}

小結

  1. 回撥函式是 JS 非同步程式設計的基本組成單元。
  2. 人的大腦是單執行緒工作的,但 JS 程式碼並沒有表現出線性關係,人很難一眼看出執行順序,如果寫的程式碼看不懂,那就容易出bug
  3. 我們需要一種符合人腦思維的程式碼書寫形式來表達非同步
  4. 使用各類庫實際上在來回交換回撥的控制權,這就會導致許多信任問題
  5. 我們不可能因為回撥的缺點就不使用它,因為那是不可能的,必須出臺一攬子計劃解決方案,未來的 JS 規範正嘗試出臺這樣的政策,即我們接下來要講的。

相關文章