天天看點

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

背景

有時我們能在Chrome開發者工具的Network tab裡觀察到SAP UI5應用會發出某些狀态為"取消"的OData請求。如下圖第五個請求。

之前有一種似是而非的說法:極短時間内發送兩個OData請求,則第一個會自動被cancel掉。

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

這個說法從字面上看,有兩點值得推敲:

1. cancel掉,被誰cancel掉?UI5架構還是Chrome?

2. “極短”,多短算極短?

我用代碼在for循環裡一共發10個OData請求:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

無論是同步還是異步,都沒有任何的請求被cancel。

10個同步請求:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

10個異步請求:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

就算發100個request都不會有一個request被cancel:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

驗證結果,之前的說法“極短時間内發送兩個OData request,前一個會自動被cancel掉”是錯誤的。

那再回到本文第一張圖觀察到的cancel的場景, 原因究竟是什麼?

觀察産生了被取消的OData請求的應用代碼,觀察到第523行有這個refresh操作:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

在這個方法的第601行,bChangeDetected變量為true導緻abortPendingRequest的調用。

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

abortPendingRequest的注釋已經很清楚地說明問題了。

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

什麼情況下會導緻AbortPendingRequest? 直接使用Chrome開發者工具的全文搜尋得到答案:OData model的三個API: filter, sort, refresh

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

下面是我的同僚Li Ben的進一步補充。

關于這個現象發生的原因和條件的問題

1. 在哪裡可以看到這個cancel現象?

在我們的live search功能上,如果輸入較快或者正常速度輸入,會看到前面很多輸入請求都會被cancel掉:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

如果輸入較慢則不會:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

真的是快慢的原因嗎?

仔細觀察network發現,真正的原因是當上一次的network還處于pending狀态的時候,繼續輸入發起的請求就會cancel掉上一次的請求:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

繼續深究, 這是在哪裡做到的?

在SAP UI5的OData架構裡面有這樣的實作:

在ODataModel.js中維護了一個http request的pending list,将已經發送但是還沒有收到響應的request對象都緩存在這個清單中:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

每次發起OData請求的時候都會調用ODataModel的_request()方法,這個方法會把目前的request加到pending list中,并且通過一個wrap method包裝回調函數,確定在響應傳回的時候首先把緩存的request對象從pending list中拿掉:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

每次在OData Model上發起filter, sort, refresh操作的時候,都會檢查是否存在pending的request對象,如果存在未完成的請求,abort掉它:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

回答上面的問題,在什麼情況下會發生這種現象?

1. 同一個ODataModel的instance上發出的連續請求,因為pending list是緩存在this級别上面的。

2. 前一個Http請求的network還處于pending status的時候。

3. 就讀ODataModel的代碼和觀察到的現象,在ODataModel上發起filter, sort或者refresh的時候。

為什麼在OData的request對象上發起abort調用就可以取消底層的network call?

簡單的說,UI5裡面的OData Request對象是底層的Ajax Request對象XmlHttpRequest的一個代理,在ODataModel的_submit方法中:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

具體實作是UI5中利用了一個第三方的庫datajs,datajs最終會調用浏覽器的底層http對象XMLHttpRequest:

謠言粉碎機 - 極短時間内發送兩個Odata request,前一個會自動被cancel掉?

繼續閱讀