# 問題說明

前陣子看到同事 A 與老闆不斷爭執一個內部系統的其中一個問題,一問才知道這個問題已經卡了好幾個月還無解。

問題之所以困難是因為它是偶發性的,可能今天突然正常,沒幾天後又死當且發生機率在每個人的電腦也不一樣,日誌上卡這個 Task 卡了好幾個月,問題描述也只能掛著各種臆測造成死當的工作流程以及錯誤訊息

很難觸發問題,也很難去解決問題,打開 console 看也始終只能看到 Uncaught TypeError: Cannot read property 'xxx' of undefined 錯誤,但是卻不知道是哪裡的 xxx 會是 undefined

# 解決方法

某天亦然興起嘗試幫忙解看看這萬年 BUG,結果出乎意料的不到一個上午就解決了,萬萬沒想到網路會是影響此錯誤發生的機率。也完美解釋為何同事 A、老闆、以及我的電腦上發生的機率為由低至高的這個荒謬現象

同事 A 的電腦試出錯的機率 趨近於 0 ,老闆的電腦試出錯的機率約 90% 上下,而我則是介於兩者之間。

先看程式碼的部分如何修改:

stuff_fact_grid = new Ext.grid.EditorGridPanel({
  ............................
  listeners: {
    beforeedit: function (editor, context) {
      // console.log('editor:', editor);
      if (!editor.record || typeof editor.record === "undefined") {
        return false;
      }
      if (editor && !editor.record && editor.row < stuff_fact_ds.getCount()) {
        // 沒有修改的資料,就新增一筆
        editor.record = stuff_fact_ds.getAt(editor.row);
      }
    },
  },
});
.............................
stuff_fact_grid.on('afteredit', UpdateData); //修改處理

這邊在 stuff_fact_grid(EditorGridPanel Object)多加了一個 listerner 來監聽 beforeedit 事件,並且在這個事件中加入了一個判斷式,如果 editor.recordundefined 就直接回傳 false,這樣就可以避免錯誤發生。

並將原本 UpdateData 的 function 用 async await 改寫:

// 舊的 UpdateData function
function UpdateData(oGrid) {
  if (!oGrid.record || !oGrid.record.data) {
    return; // 如果 oGrid.record 或 oGrid.record.data 為 undefined 或 null,則退出函數
  }
  // console.log(oGrid);
  var temp = oGrid.record.data;
  Ext.Ajax.request({
    .............
    success: function (response) {
      ......
    },
    failure: function (response) {
      ......
    },
  });
}

改寫成如下:

// 新的 UpdateData function
async function UpdateData(oGrid) {
  // console.log(oGrid.record);
  if (!oGrid.record) {
    return; // 如果 oGrid.record 或 oGrid.record.data 為 undefined 或 null,則退出函數
  }
  var temp = oGrid.record.data;
  await Ext.Ajax.request({
    .............
    success: function (response) {
      // 處理成功的回應
    },
    failure: function (response) {
      // 處理失敗的回應
    },
  });
}

除了將原本的 Ext.Ajax.request 改成 async 的異步函數,也多寫了一個判斷當 oGrid.recordundefined 時,就直接退出函數。

# 結論

造成此錯誤的主因:在還沒拿到資料前進行改動作就送出所以發生context undefined。網速越快=執行動作越快=越容易出現後端來不及動作,前端就嘗試回傳尚未處理的 undefined 資料

  • 網速影響機率:老闆網路 10G > 我的 2.5G > 同事 1G
    • 老闆的電腦直接跟公司 10G HUB 對連,所以網速最快,發生錯誤的機率也最高。

  • 沒有使用異步函數:關於異步函數的部分,網路上已經有很多文章解釋了,這邊就不多做介紹。
  • 程式碼沒有錯誤例外處理:就算程式碼已經寫到天衣無縫,一樣要寫錯誤例外處理,不然就會再次遇到錯誤發生,卻不知道是哪裡 xxx undefined

# 補充說明 - ExtJS EditorGridPanel 事件觸發順序

關於 ExtJS 的 EditorGridPanelbeforeedit 事件與 afteredit 事件的觸發順序:

# beforeedit 事件

beforeedit 事件在單元格開始編輯之前觸發。它提供了一個機會來取消編輯操作或對當前單元格的值進行驗證。beforeedit 事件處理程序接收一個包含以下屬性的對象:

  • record:當前編輯的數據行對象
  • field:被編輯的字段名稱
  • value:被編輯字段的當前值
  • row:被編輯行的索引
  • column:被編輯列的索引

當返回 false 時,beforeedit 事件將取消編輯操作。

# afteredit 事件

在單元格編輯完成並且新值已經設置到數據行之後,afteredit 事件將被觸發。此時,您可以將新值保存到服務器或進行其他處理。afteredit 事件處理程序接收一個包含以下屬性的對象:

  • record:當前編輯的數據行對象
  • field:被編輯的字段名稱
  • value:被編輯字段的新值
  • originalValue:被編輯字段的原始值
  • row:被編輯行的索引
  • column:被編輯列的索引

# 觸發順序

以下是 beforeeditafteredit 事件的觸發順序:

  1. 用戶單擊單元格以開始編輯。
  2. beforeedit 事件被觸發。如果返回 false,則取消編輯操作;否則,繼續進行編輯。
  3. 用戶完成編輯並單擊其他地方或按下 Enter 鍵。
  4. afteredit 事件被觸發。這時可以將新值保存到服務器或進行其他處理。

範例程式碼:

var editorGrid = new Ext.grid.EditorGridPanel({
    store: myStore,
    columns: myColumns,
    ...
    listeners: {
        beforeedit: function(e) {
            // 可以在此進行驗證或其他操作
            // 如果返回 false,則取消編輯操作
        },
        afteredit: function(e) {
            // 在此處保存新值到服務器或進行其他處理
        }
    }
});

請我喝[茶]~( ̄▽ ̄)~*

Young 微信支付

微信支付

Young 支付寶

支付寶