聊一聊 JavaScript 中的錯誤隔離

1932723078 8年前發布 | 6K 次閱讀 JavaScript開發 JavaScript

接口請求失敗、接口中部分數據缺失、運營數據不符合預期… 當我們的應用發布上線后,就開始面臨這些風險。

而一旦這些問題導致了 JavaScript 報錯(如空指針異常),并且沒有被有效地隔離,就有可能引發頁面的白屏、無法交互等線上問題。

在雙 11 準備期間,我們收集了過往一年前端相關的線上問題,在收集的 21 個案例中,竟有一半的問題都與「數據異常觸發頁面顯示異常」這個原因有些相關。

如何將錯誤的影響隔離在一定范圍內,顯得尤為重要。

這篇文章就和大家一起來聊一聊我們嘗試過的一些方案,及遇到的問題。

從空指針異常說起

數據引發的最常見的問題就是空指針異常。

var result = a.b.c.d;

這樣的代碼如同地雷,一旦 a 是一個動態數據,那么問題一觸即發。

封裝一個 get 的方法來取值,當數據不存在時,返回 undefined ,可以快速避免此類問題。

var result = get(a, 'b.c.d');

但如同我們期望大家在取值前,都先做判斷一樣,并不能保證所有人都這么用了,用不用全靠自覺。

if (a && a.b && a.b.c) {
    var result = a.b.c.d;
}

所以,有了以下的一些方案:

異步數據校驗

對異步數據校驗的想法是,在數據獲取后、使用前,先做一遍schema校驗,檢測重要數據缺失、類型不對等異常情況。

與此方案對應的,我們在 fetch 的基礎上封裝了 fetch-checker 注1 組件。

fetch-checker 強制要求用戶在請求數據的同時,提供數據對應的 schema:

let schema = {
    "rule": {
      "type": "string",
    },
    "banner": {
      "type": "object",
      "required": true,
      "default": {
        "url": "https://item.taobao.com/item.htm?id=527331762117"
      }
    }
};

這份 schema 需要描述:

  • 每個字段的類型
  • 字段是否 required
  • 當 required 的字段缺失時,是否需要打底數據

fetch-checker 在拿到數據后,先做一層校驗,如有需要的話,補上缺失的數據,然后再返回給調用者。這樣,使用者拿到的數據就一定是符合預期的。

然而,這個方案面臨的挑戰是:

  1. 如何確保調用者提供了完整的 schema 描述。不想寫 schema,完全可以提供一個粗略的 schema 描述,來通過校驗。
  2. schema 如何精簡。即不會對 bundle 大小造成太大影響,又能滿足校驗的功能。

代碼編譯

受 babel 的啟發,這個方案是對存在 NPE 隱患的代碼,在編譯階段,將其轉換成等價的安全代碼。如下所示:

var a = {};

// input
var result = a.b.c;

// output
var result = (_object2 = (_object3 = a) == null ? null : _object3.b) == null ? null : _object2.c;

當 a 為空對象時,執行編譯后的代碼會返回 null ,從而避免因為代碼拋錯,阻斷后續進程。

在 babel-plugin-safe-member-expression 注2 這個 Babel 插件中,我們做了上述的嘗試。目前,cake項目中,已經可以通過 enableSafeMemberExpression 這個配置,選擇性的啟用該功能。

這個方案相比來說接入成本較低,開發者無需對現有的代碼做出調整,但同樣存在挑戰:

  • 開發階段問題不易暴露,明明應該報錯的場景,卻沒有任何反饋。理想的狀態是:開發調試階段盡可能多的暴露問題,線上則盡可能的減少報錯。
  • 隱患的代碼如何界定。目前所有的 a.b 的調用方式都會按上述方案進行編譯,雖然測試過程中還沒有發現問題,但只處理有隱患的代碼才更安全。

靜態校驗

以 flow 為代表的靜態校驗工具,可以在一定程度上檢測出 NPE 隱患。

type res = {
    data ?: Object
}

let name = res.data.name;
// property `name`. Propery cannot be accessed on possibly undefined value

如上面的代碼所描述的,使用者需要首先理清自己的數據是否允許為空值,當 data 被允許為空值時,通過 flow 檢測, data.name 類似這樣調用便會被檢測出錯誤。

然而,如何來推進所有的業務都接入靜態校驗,接入后,又如何保證開發者描述了所有的類型,卻同樣是個難點。

小結

總結以上幾種方案,各有優缺點,都還不能算做最理想的解決方案。

方案名稱 優勢 缺點
提前判斷 實行簡單 全靠自覺
異步數據校驗 可確保所使用的數據是滿足預期的 schema 描述成本高
代碼編譯 接入成本低,易執行 開發階段不易暴露問題
靜態校驗 對現有代碼邏輯侵入少 落地成本高

對于業務來說,最愿意使用和有效的方案一定是:

  • 能將線上問題隔離在一個小范圍內,同時不影響開發調試階段的問題暴露
  • 能提前暴露出隱患
  • 接入成本低,不需要大量修改現有業務代碼

關于空指針異常和錯誤隔離,機智的你又有哪些方案,一起來討論吧。

 

來自:http://taobaofed.org/blog/2016/11/10/prevent-prop-access-error-in-js/

 

 本文由用戶 1932723078 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!