Ext JS 筆記:異步函數(async/await) 的重要性

網速竟然也會影響發生錯誤的機率

Posted by Young on 2023-01-10
Estimated Reading Time 6 Minutes
Words 1.6k In Total
Viewed Times

問題說明

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

問題之所以困難是因為它是偶發性的,換言之,有可能今天突然正常,但沒幾天後又死當且發生機率在每個人的電腦也不一樣,此外,這個問題在某些電腦上能夠正常運作,但在其他電腦上卻無法運行。

👴 老闆:「 又卡?第一個流程步驟就卡住,也不用特定去重複操作好幾次。」

🧑‍💻 同事 A:「 可是在我這台電腦怎麼試都沒有問題阿。 」

👴 老闆:「 真的不行就整個架構重寫,不然能怎麼辦?總不能用一次卡一次 」

🧑‍💻 同事 A:「 我試試看修改幾個變數名稱了…,也會進行單元測試和集成測試。」

數日後…

👴 老闆:「 測試第一個就掛了!要不要交叉測試一下是不是跨表查詢導致資料衝除? 」

🧑‍💻 同事 A:「 我試都正常阿!這次換修改物件名稱試試了…」

時隔許久…工作日誌上就卡著這個 Task 卡了好幾個月,問題描述也只能掛著各種臆測造成死當的工作流程以及錯誤訊息,而同事 A 跟老闆也都心知肚明,光修改幾個名稱是無法解決問題的 XD。

試不出問題,根本也不用談如何去解決問題,打開 console 看也始終只能看到 Uncaught TypeError: Cannot read property 'xxx' of undefined 這種錯誤,但是卻不知道是哪裡的 xxx 會是 undefined

解決方法

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 改寫:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 舊的 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) {
......
},
});
}

改寫成如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 新的 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 事件被觸發。這時可以將新值保存到服務器或進行其他處理。

範例程式碼:

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

若您覺得這篇文章對您有幫助,歡迎分享出去讓更多人看到⊂◉‿◉つ~


留言版