1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        聊一聊 JavaScript 中的錯誤隔離

        共 2483字,需瀏覽 5分鐘

         ·

        2021-02-24 18:58


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

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

        在雙 11 準備期間,我們收集了過往一年前端相關(guān)的線上問題,在收集的 21 個案例中,竟有一半的問題都與「數(shù)據(jù)異常觸發(fā)頁面顯示異?!惯@個原因有些相關(guān)。

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

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

        從空指針異常說起

        數(shù)據(jù)引發(fā)的最常見的問題就是空指針異常。

        var result = a.b.c.d;

        這樣的代碼如同地雷,一旦 a 是一個動態(tài)數(shù)據(jù),那么問題一觸即發(fā)。

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

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

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

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

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

        異步數(shù)據(jù)校驗

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

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

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

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

        這份 schema 需要描述:

        • 每個字段的類型

        • 字段是否 required

        • 當 required 的字段缺失時,是否需要打底數(shù)據(jù)

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

        然而,這個方案面臨的挑戰(zhàn)是:

        1. 如何確保調(diào)用者提供了完整的 schema 描述。

          不想寫 schema,完全可以提供一個粗略的 schema 描述,來通過校驗。

        2. schema 如何精簡。

          即不會對 bundle 大小造成太大影響,又能滿足校驗的功能。

        代碼編譯

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

        var a = {};

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

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

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

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

        這個方案相比來說接入成本較低,開發(fā)者無需對現(xiàn)有的代碼做出調(diào)整,但同樣存在挑戰(zhàn):

        • 開發(fā)階段問題不易暴露,明明應該報錯的場景,卻沒有任何反饋。

          理想的狀態(tài)是:

          開發(fā)調(diào)試階段盡可能多的暴露問題,線上則盡可能的減少報錯。

        • 隱患的代碼如何界定。

          目前所有的 a.b 的調(diào)用方式都會按上述方案進行編譯,雖然測試過程中還沒有發(fā)現(xiàn)問題,但只處理有隱患的代碼才更安全。

        靜態(tài)校驗

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

        type res = {
        ? ?data ?: Object
        }

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

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

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

        小結(jié)

        總結(jié)以上幾種方案,各有優(yōu)缺點,都還不能算做最理想的解決方案。

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

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

        • 能將線上問題隔離在一個小范圍內(nèi),同時不影響開發(fā)調(diào)試階段的問題暴露

        • 能提前暴露出隱患

        • 接入成本低,不需要大量修改現(xiàn)有業(yè)務代碼

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

        編者注: 本文提到的所有工具如未提供鏈接,可能是內(nèi)部代碼,暫未對外公布。

        如果你喜歡探討技術(shù),或者對本文有任何的意見或建議,非常歡迎加魚頭微信好友一起探討,當然,魚頭也非常希望能跟你一起聊生活,聊愛好,談天說地。魚頭的微信號是:krisChans95 也可以掃碼關(guān)注公眾號,訂閱更多精彩內(nèi)容。
        瀏覽 52
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            区一区二区三视频 | 伊人在线大香蕉日比 | 久久久久亚洲精品成人无码 | 成人在线视频网址 | 亚洲美女色禁图 | 口述一女二男三p经过 | 三级做爰的全部 | 天天日天天干天天射天天操 | 四虎4hu新入口av | 国产黄色视频在线看 |