在【Async callback in JS DataSnap Framework】裡有提到可以使用【Handling the Result】,也就是Callback
function,如此就能避免掉在XMLHttpRequest中已被棄用的【同步請求(Synchronous
request)】。
不過呢,有一好就沒有二好,寫著寫著,我的程式碼就變得和下圖一樣:
傳說中的回呼地獄(Callback hell) 圖:取自網路 |
XHRHttpRequest正常流程
JavaScript DataSnap framework 會自動產出 ServerFunction.js
檔,以【ReverseString】為例,程式碼部分片段如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* * @param Value [in] - Type on server: string * @return result - Type on server: string */ this.ReverseString = function(Value) { var returnObject = this.executor.executeMethod('ReverseString', "GET", [Value], arguments[1], true, arguments[2], arguments[3]); if (arguments[1] == null) { if (returnObject != null && returnObject.result != null && isArray(returnObject.result)) { var resultArray = returnObject.result; var resultObject = new Object(); resultObject.Value = Value; resultObject.result = resultArray[0]; return resultObject; } return returnObject; } }; |
按照範例來看,最終的resultObject會有【Value】和【result】兩個屬性,分別儲存【傳出值】和【回傳值】,官方範例裡的ServerFunctionInvoker.html,會將這兩個屬性一起展示以方便我們理解。
程式碼和運行結果會是:
1 2 3 4 5 6 7 8 |
function onReverseStringClick() { var valueField = document.getElementById('valueField'); var s = serverMethods().ReverseString(valueField.value); console.log(`Send Value is : ${s.Value}; Result data is : ${s.result}`); valueField.value = s.result; } // Send Value is : A B C; Result data is : C B A |
可以看出回傳的物件有Value和result兩個屬性 |
理想終究是理想,太神化了
就在Server斷線再開時,就遭遇到傳說中的【undefined】:
不僅回傳值沒有,就連輸入值也沒有 |
因為回傳的沒有【result】,自然也就不會走建立屬性流程 |
回傳結果是一個例外內容,在範例中沒提到,只有遇到才會知道。(拭淚)
XHRHttpRequest小結
官方操作API的範例在正常流程下沒有問題,而且相當好理解,只是同步應用的方式已被列為棄用,應當避免再使用同步AJAX。
由於【403
SessionExpired】是開發DataSnap服務時非常容易遇到的錯誤,所以使用【Promise
fetch】時,除了正常流程外,例外流程也務必要設計進去。
Promise fetch 開發前
官方有寫出完整且詳盡的XHRHttpRequest設計內容,這可能是為了相容於IE系列所寫出的。行動平台的瀏覽器大多有支援最新的JavaScript標準,故直上fetch是沒有問題的。
在操作fetch之前,先來看看XHRHttpRequest發出API請求時的詳細內容:
關鍵在於【Authorization】和【Pragma】這兩個Header參數:
Authorization:驗證方式【Basic】和BASE64化的帳號密碼
Pragma:傳送SessionID,可以透過它在DataSnap
Server裡進行狀態管理,Server關閉時,Session
Manager(TDSSessionManager)也會被清空,這點要留意!
得知要注意的地方後,就可以使用fetch來如法泡製。
fetch基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | "use strict" function fetchAsyncReverseString() { let valueField = document.getElementById("valueFieldAsync"); fetch(`http://localhost:8080/datasnap/rest/TServerMethods1/ReverseString/${valueField.value}`, { method:'GET', headers:{ 'Accept':'application/json', 'Content-Type':'text/plain;charset=UTF-8', 'Authorization':'Basic Og==', 'pragma':`dssession=${getSessionID()}` } } ) .then( function (response) { if (!response.ok) throw Error(response.statusText) return response.json() } ) .then( function (returnObject) { let dataResult='' if (returnObject !== null && returnObject.result !== null && isArray(returnObject.result)) { dataResult = returnObject.result[0] } valueField.value = dataResult } ) .catch( error => console.error(error) ) } |
Promise只需要使用【then】和【catch】就能完成所有工作,也就不會有Callback hell的悲劇發生。
瀏覽器記錄的fetch Request內容:
可以看到操作fetch和xhr分別發送request |
fetch header內容也可以和xhr相同 |
錯誤訊息也可以被正確取出 |
因為fetch就是要從零開始刻,所以程式碼很長是必然的。
有了官方XHRHttpRequest範例的經典,fetch也就可以順利的設計出來。
同步傳輸影響到UX
Promise的核心概念是非同步傳輸,在Facebook中的動態訊息也是用到大量非同步傳輸,如果要等動態訊息全部載入再顯示,那使用者可能會等到抓狂。
但如果是編輯資料時先顥示輸入框一陣子,等資料下載完成後再填入到輸入框給使用者修改,那使用者會為了系統給出空白資料而感到困惑。
所以有必要另外寫一支同步傳輸的函式。
同步傳輸?那不是已被棄用了嗎?
XHRHttpRequest同步傳輸雖然被棄用,但上帝關了一扇窗,必定會開一道門
並沒有。
在fetch中,可以用另一種方式達到類似同步傳輸效果:await。
**await故名思義是【等待】之義,它會等待(async)Promise函式完成後返回【解決 ( resolve )】 或【出錯 ( reject )】 後才會進行下一步,故不完全是【同步】之義。
改寫同步傳輸函式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | "use strict" async function fetchSyncReverseString() { let valueField = document.getElementById("valueFieldSync"); const response = await fetch(`http://localhost:8080/datasnap/rest/TServerMethods1/ReverseString/${valueField.value}`, { method:'GET', headers:{ 'Accept':'application/json', 'Content-Type':'text/plain;charset=UTF-8', 'Authorization':'Basic Og==', 'pragma':`dssession=${getSessionID()}` } } ) if (!response.ok) throw Error(response.statusText) let returnObject = await response.json() let dataResult='' if (returnObject !== null && returnObject.result !== null && isArray(returnObject.result)) { dataResult = returnObject.result[0] } valueField.value = dataResult; } |
使用新標準的Promise fetch就能滿足同步/非同步兩種要求。
以上資訊提供給各位參考,謝謝各位收看,我們下次見。
***下回預告:DataSnap 狀態管理初探。***
沒有留言:
張貼留言