如果被耗時任務拖累,可能是姿勢不對
- 2019 年 12 月 4 日
- 筆記
本文作者:IMWeb helinjiang 原文出處:IMWeb社區 未經同意,禁止轉載
如果被耗時任務拖累,可能是姿勢不對
在業務中,有時候需要處理一些相對耗時的事情,而且還有一些其他的邏輯還可能會依賴這個耗時任務。誠然,太久的耗時會對用戶體驗不好。
本文就自己在業務中的一次實踐,從其中一個小角度來分享看法,純屬個人觀點,如有紕漏之處,還望指教。
背景
我們的項目是Hybrid混合應用,頁面運行在手機QQ(後續簡稱手Q)中。在我們的業務中,我們有個新上線的業務,進入頁面A之後,需要根據用戶的地理位置(可以用快取)去跳轉到新業務頁面B(灰度)或者繼續渲染頁面A。

最正經的實現方案
很容易想到的最正(diao)經(si)的一種實現方案,就是先獲得當前用戶的地理位置,拿到地理位置之後,還要去調用後台CGI介面,獲得當前用戶處於的城市,然後再根據這個城市,判斷是否應該跳轉到特定的頁面。
偽程式碼實現
使用偽程式碼表示如下:
// 調用手Q介面獲取當前用戶的地理位置,data中包含了經度和緯度 getLocation(function(data){ // 調用CGI介面,獲得當前的城市資訊 getCity(data, function(res){ if(res.city=='深圳') { // 跳轉到頁面B jumpTo('pageB'); } else { // 繼續渲染頁面A init(); } }); });
你真不是來搗亂的吧
理論是美好的,但是…
- 客戶端的
getLocation
介面去獲取用戶當前地理位置,其實是非常耗時的操作。經過多次測試,獲取一次的耗時在2s
~5s
左右,即便可以使用其快取,也需要花費200ms
以上,而且還很不穩定。要知道當時測試的環境是 wifi 下,而且測試機是比較新的配置中等的小米4。如果真實用戶拿著一個配置不高的手機,然後在網路狀況不好的場景下,我猜測耗時會更高。 - 除了客戶端的介面會耗時之外,還需要調用一次後台CGI,由於這個CGI介面已經在現網運行過一段時間,從檢測的數據來說,wifi下請求一次耗時大概在
50ms
~150ms
左右,如果在非wifi場景下,這個時間肯定會更耗時。 - webview本身初始化時間、js/css/img文件、其他CGI請求等的耗時等累積也在幾百毫秒以上。
綜上,如果用戶第一次沒有快取的情況下,或者網路狀況不好,或者他用的手機屬於比較親(di)民(duan)的那種,那麼,當用戶看著載入的菊花圖一直轉啊轉,耳邊不禁想起「跟著我左手右手一個慢動作,右手左手慢動作重播」時,我想應該沒有多少人是會有耐心等待的。
進一步分析
上文討論的最常規的方案顯然會有體驗的問題,怎麼辦?
需要時效性嗎
對我們造成困擾的緣故,在於我們常會有一種程式設計師的思維(情懷),認為流程就是這樣啊,這樣才能夠實現功能,而且是最精確最實時的。你看每次你進來我們的頁面我們就對你進行一次判斷,避免上一分鐘你在城市A,下一分鐘到了城市B會出問題。
沒錯,追求精確和完美並不是壞事,但這會付出時間成本,在這裡而言,我們真的那麼需要時效性嗎?80% 以上的用戶很少會在幾小時或一天內會離開一座城市;即便離開了,看得到我們的新頁面了,也沒任何關係,更何況我們是在灰度,多幾個人看到關係不大,以後遲早大家都看得到。
快取地理位置
既然沒有時效性的要求,要處理的業務也不是敏感,那麼就快取唄。且不是說快取客戶端的 getLocation
結果,而是直接快取最後計算得到的城市名。
if (!existCity) { // 調用手Q介面獲取當前用戶的地理位置,data中包含了經度和緯度 getLocation(function(data) { // 調用CGI介面,獲得當前的城市資訊 getCity(data, function(res) { if (res.city == '深圳') { // 跳轉到頁面B jumpTo('pageB'); } else { // 繼續渲染頁面A init(); } }); }); } else { // 繼續渲染頁面A init(); }
用戶的第一次
即便我們可以快取地理位置,那用戶第一次進來沒有快取時,依然逃脫不了載入緩慢的命運。難道要告訴用戶說「忍一忍,第一次都會痛苦的,下一次再來時你就會感到暢快了」?對用戶的第一次不負責,用戶可能就不會給你第二次了。
提前準備快取
既然在用戶要的時候無法滿足他們的需求,那麼,何不提前準備呢?比如在一個浪漫溫馨你儂我儂的夜晚,氣氛恰到好處,卻發現缺少了「必要的東西」,這時候需要你大晚上下樓跑幾條街的店裡去貢獻一點GDP,你會崩潰的。
回到我們說的場景,似乎也可以提前發一個版本,在這個版本中,增加一個小功能,就是在用戶正常打開頁面之後,再私下去獲取到用戶的位置,並放入到本地 localStorage
中快取結果。等到真實版本發出之後,由於之前已經有快取結果了,那麼就省略了調用介面的過程,而換成了判斷 localStorage
快取了,這個性能立即就上來了。
先領劵,再享受優惠
提前準備快取的辦法理論上是行得通的,但麻煩啊,要新發版本,而且為了一小部分灰度的人,影響了大部分人的體驗(新版本發了之後之前的離線包或js文件快取就失效了,何況他們又享受不到新的業務,浪費),有點不值得。就比如「必要的東西」是準備了,但沒機會使用,也就浪費了。
進一步分析,還可以有進一步的優化。簡單而言,就是用戶第一次進來時,全都展示原始的頁面A後,後台開始獲取城市資訊,並打上快取(發放優惠劵,但也不是所有人都發,比如乞丐之類的就忽略了);第二次之後再來時,有指定的快取了就直接跳轉到頁面B了(憑藉優惠券享受優惠)。實際上我們最終的方案就是這種。
最後的方案和總結
上面說到了我們最終的方案是「先領劵再享受優惠」的思路。其實這和離線包的機制是類似的,如果本地沒有離線包,則返回線上的,同時手Q後台執行緒會去拉取離線包到本地;等第二次再來時,由於有了離線包,這時候就直接使用離線包的內容,提升了用戶體驗。
很羅嗦的記錄了最近在某個業務中遇到的情況,我想表達的內容也比較簡單,就是如果我們依賴了很耗時的業務時,可以換種思維,換種姿勢,例如將同步處理修改為非同步處理,或者思考其他的方式,來解決技術無法解決問題。很典型的還有我們常見的進度條或者菊花loading圖,也是從另外的方面的努力,來「掩蓋」耗時的問題,而耗時問題有時候是無法避免的。