問題說明
前陣子看到了同事 A 與老闆不斷爭執一個內部系統的其中一個問題,一問才知道這個問題已經卡了好幾個月還無解。
問題之所以困難是因為它是偶發性的
,換言之,有可能今天突然正常,但沒幾天後又死當且發生機率在每個人的電腦也不一樣,此外,這個問題在某些電腦上能夠正常運作,但在其他電腦上卻無法運行。
👴 老闆:「 又卡?第一個流程步驟就卡住,也不用特定去重複操作好幾次。」
🧑💻 同事 A:「 可是在我這台電腦怎麼試都沒有問題阿。 」
👴 老闆:「 真的不行就整個架構重寫,不然能怎麼辦?總不能用一次卡一次 」
🧑💻 同事 A:「 我試試看修改幾個變數名稱了…,也會進行單元測試和集成測試。」
數日後…
👴 老闆:「 測試第一個就掛了!要不要交叉測試一下是不是跨表查詢導致資料衝除? 」
🧑💻 同事 A:「 我試都正常阿!這次換修改物件名稱試試了…」
時隔許久…工作日誌上就卡著這個 Task 卡了好幾個月,問題描述也只能掛著
各種臆測造成死當的工作流程
以及錯誤訊息
,而同事 A 跟老闆也都心知肚明,光修改幾個名稱是無法解決問題的 XD。
試不出問題,根本也不用談如何去解決問題,打開 console 看也始終只能看到 Uncaught TypeError: Cannot read property 'xxx' of undefined
這種錯誤,但是卻不知道是哪裡的 xxx
會是 undefined
。
解決方法
某天亦然興起決定(雞婆)嘗試幫忙解看看這萬年 BUG,結果出乎意料的花不到一個上午就解決了,萬萬沒想到網路竟會是影響此錯誤發生的機率。也完美解釋了為何在同事 A、老闆、以及我的電腦上發生的機率剛好為由低至高的這個荒謬現象。
同事 A 的電腦試出錯的機率 趨近於 0 ,老闆的電腦試出錯的機率約 90% 上下,而我則是介於兩者之間。
先來看看程式碼的部分如何修改:
1 | stuff_fact_grid = new Ext.grid.EditorGridPanel({ |
這邊在 stuff_fact_grid(EditorGridPanel Object)
多加了一個 listerner 來監聽 beforeedit
事件,並且在這個事件中加入了一個判斷式,如果 editor.record
是 undefined
就直接回傳 false
,這樣就可以避免錯誤發生。
並將原本 UpdateData 的 function 用 async await 改寫:
1 | // 舊的 UpdateData function |
改寫成如下:
1 | // 新的 UpdateData function |
這邊除了將原本的 Ext.Ajax.request
改成 async
的異步函數,也多寫了一個判斷當 oGrid.record
為 undefined
時,就直接退出函數。
結論
在還沒拿到資料前進行改動作就送出所以發生context undefined
。網速越快=執行動作越快=越容易出現後端來不及動作,前端就嘗試回傳尚未處理的 undefined 資料。
造成此錯誤的主因:
網速影響機率
:老闆網路 10G > 我的 2.5G > 同事 1G-
老闆的電腦直接跟公司 10G HUB 對連,所以網路速度最快,發生錯誤的機率也最高。
-
- 沒有使用
異步函數
:關於異步函數的部分,網路上已經有很多文章解釋了,這邊就不多做介紹。 - 程式碼沒有
錯誤例外處理
:就算程式碼已經寫到天衣無縫,還是一樣要寫錯誤例外處理,不然就會像這個問題一樣,錯誤發生了,但是卻不知道是哪裡的xxx undefined
。
補充說明 - ExtJS EditorGridPanel 事件觸發順序
關於 ExtJS 的 EditorGridPanel
的 beforeedit
事件與 afteredit
事件的觸發順序:
beforeedit 事件
beforeedit
事件在單元格開始編輯之前觸發。它提供了一個機會來取消編輯操作或對當前單元格的值進行驗證。beforeedit
事件處理程序接收一個包含以下屬性的對象:
record
:當前編輯的數據行對象field
:被編輯的字段名稱value
:被編輯字段的當前值row
:被編輯行的索引column
:被編輯列的索引
當返回 false
時,beforeedit
事件將取消編輯操作。
afteredit 事件
在單元格編輯完成並且新值已經設置到數據行之後,afteredit
事件將被觸發。此時,您可以將新值保存到服務器或進行其他處理。afteredit
事件處理程序接收一個包含以下屬性的對象:
record
:當前編輯的數據行對象field
:被編輯的字段名稱value
:被編輯字段的新值originalValue
:被編輯字段的原始值row
:被編輯行的索引column
:被編輯列的索引
觸發順序
以下是 beforeedit
和 afteredit
事件的觸發順序:
- 用戶單擊單元格以開始編輯。
beforeedit
事件被觸發。如果返回false
,則取消編輯操作;否則,繼續進行編輯。- 用戶完成編輯並單擊其他地方或按下 Enter 鍵。
afteredit
事件被觸發。這時可以將新值保存到服務器或進行其他處理。
範例程式碼:
1 | var editorGrid = new Ext.grid.EditorGridPanel({ |
若您覺得這篇文章對您有幫助,歡迎分享出去讓更多人看到⊂◉‿◉つ~
留言版