如今說一下何爲「競態「吧。
ajax
好比有個產品列表,點擊產品列表上的產品圖片,而後頁面上出現一個彈窗查看產品詳情。想詳情是經過產品圖片對應的id去後端接口獲取到的。當你點擊一個產圖片,在數據沒有返回以前,你又點擊了另一張圖片,或者另外兩張圖片,你最終可能看到了一個本身不想要的產品詳情。這個產品詳情可能來自於你的第一次點擊,或者你的第二次點擊。而你指望看到的是最後一次點擊的詳情,你最後一次點擊的產品詳情可能會在你看到的最終畫面以前一閃而過。npm
咱們把這樣的場景定義爲「競態」現象。後端
固然若是出現了這樣的現象,欣仔認爲交互設計確定是不合格的。。。由於這不光是技術層面的問題,更多的是整個場景設計的問題。api
本篇的目的也是從技術的角度去解決可能會出現的這類問題。瀏覽器
以上的場景出現的一個C/S 結構的SPA的應用中,全部操做都不會對瀏覽器產生刷新動做。bash
說到SPA,之前有個少年對按摩比較感興趣,特別是泰式按摩,他有一個想成爲世界頂級泰式按摩技師的夢想,但限制於條件,初中沒畢業就到社會上闖蕩,夢想成爲夙願久久不能觸及。一次偶然的機會,他在上網的時候看到一則名爲「SPA應用入門到大神,只要588」的廣告。因爲初中肄業,加上他對SPA的印象,思考片刻後就點開了報名。後來他學成歸來,成爲了一名VUE 的開發人員,月薪5000,比作服務員的時候輕鬆很多。後來聽說菲律賓馬尼拉那邊看中了他的才幹 ,高薪應聘了他過去開發系統軟件,以後就再也沒聽過他的消息app
說完少年的故事,迴歸這篇文章的吧。框架
關於競態的問題,rxjs有着他本身的解決方案,也是你們通用的方案,你們能夠百度或者谷歌一下,你會看到關於rxjs在此方面的一些特性。但欣仔寫做有個原則:專挑大家沒見過的。
測試
PS:閱讀本文須要確保你已經擁有了OOP的相關開發經驗。
ui
面對同一個問題,每每會有不少解決方案。通用的方案方便你們交流,非通用的能夠給到你們一種別樣的思路。
我以前在npmjs上發過一個自定義事件的包
shinevent複製代碼
其中包含了兩個個主要模塊
ShinEvent複製代碼
和
ShinEventDispatcher複製代碼
ShinEvent負責封裝事件的描述並能夠傳遞消息,另一個則負責發佈事件。
回到咱們所面對的競態問題,該如何解決?
當第一次接口調用的時候,以及最後一次接口調用的時候。因爲用戶操做的時間間隔小於接口請求響應的時間,咱們最終只需處理最後一次接口數據的返回而忽略以前接口返回的數據。問題是:以前的全部接口請求已經發起,咱們不可能阻斷接口的請求,因此方案是咱們只能從請求回調裏作一些工做:即忽略最後一次請求以前的全部請求的數據回調,只處理最後一次的。
shinevent該怎麼用呢?
import {ShinEvent,ShinEventDispatcher} from 'shinEvent'
//define a new class which extends from ShinEventDispatcher
class testTarget extends ShinEventDispatcher{
init (){
this.dispatchEvent(new ShinEvent('init'));
}
}
//create a new testTarget as target
let target=new testTarget();
//define call back function at object 'this'
this.handleInit=function(e){
console.log('i am shinevent');
}
//add listener and set the special function
target.addEventListener('init',this.handleInit,this);
target.init();
//you will get print of "i am shinevent"
//remove listener
target.removeEventListener('init',this.handleInit,this);
target.init();
//you will get nothing複製代碼
以上代碼執行邏輯爲:
當我第一次執行
target.init()複製代碼
的時候獲得一個
i am shinevent複製代碼
的輸出,由於我在tatget的init方法裏面發佈了‘init’事件,而且在外部訂閱了init事件。當我第二次執行
target.init()複製代碼
的時候,我什麼也沒獲得。由於我在第二次執行他的時候,用了一個
target.removeEventListener('init',this.handleInit,this);複製代碼
移除了init的事件訂閱。
因此你們大體能夠知道,經過一個ShinEventDispatcher的實例,能夠發佈事件;並知道如何對特定的事件做出響應;以及移除事件。
應用到咱們這篇文章裏所面對的問題,思路能夠是:每當我新建一個相似ajax的接口請求,就撤銷以前的事件監聽,而後新建一個新的事件監聽,這就保證了目前的訂閱處理,都是最新的。
STEP1
定義一個處理接口的類:AjaxExecuter
import {ShinEvent,ShinEventDispatcher} from 'shinEvent'
class AjaxExecuter extends ShinEventDispatcher{
doAjax(url,data){
//do something ajax
//such as
$.ajax({
url,
data,
success:(msg)=>{
let event=new ShinEvent('getdata');
//ShinEvent的data參數用於封裝傳遞的消息。
//此案例中event.data的數據格式以下
//{msg:{}, 'pindex': pindex}
event.data={msg,...data};
this.dispatchEvent(event);
}
})
}
}複製代碼
STEP2
定義一個AjaxPool 管理類,包含以及管理以上AjaxExecuter類對象
class AjaxPool extends ShinEventDispatcher{
getList(pindex){
if(this.ae)
{
this.ae.removeEventListener('getdata',this.handleGetData,this);
this.ae=null;
}
this.ae=new AjaxExecuter();
this.ae.addEventListener('getdata',this.handleGetData,this);
this.ae.doAjax('abc.com',{pindex})
}
handleGetData(e)
{
let {data}=e;
let event=new ShinEvent('getlist');
event.data=data;
this.dispatchEvent(event)
}
}複製代碼
從上往下閱讀:
但我有一個須要根據頁碼獲取一串list的時候。經過AjaxPool對象的getList方法去,這個方法裏面用了一個AjaxExecuter 對象請求數據,在請求以前,對當前存在的AjaxExecuter的getdata事件作了一個移除。每次請求都對老的AjaxExecuter對象進行了對應事件以及對應對象的移除,以確保每次請求結果的處理都是惟一的。
當咱們在外圍處理調用的時候以下:
const AP =new AjaxPool();
AP.addEventListener('getlist',this.handleGetList,this);
this.handleGetList=e=>{
//e爲一個shinevent對象,包含了事件類型,以及消息。
//e.data爲事件夾帶的消息。爲step1中shinevent對象包含的消息體。
//
console.log(e.data);
}
AP.getList(1);
AP.getList(2);
AP.getList(3);
AP.getList(4);
AP.getList(5);複製代碼
最終咱們獲得的輸出只會有一次,即最後一次
AP.getList(5);複製代碼
這一次請求獲得的數據;相似於:
{msg:{}, 'pindex': 5}複製代碼
除非咱們的接口請求的速度大於代碼執行的速度。
介於此,咱們作了兩次封裝,可是都是基於‘發佈/訂閱’機制得以實現。實際上RXJS的內部原理也是如此。發佈/訂閱 是現代框架視覺驅動以及事件驅動的內核,數據雙向綁定或是單項綁定的實現原理中都能見到他的影子。
若是你們有興趣能夠經過
npm install --save shinevent//或者yarn add shinevent複製代碼
安裝測試,在npmjs搜索shinevent 獲得相關的註釋。實際上是一個很簡單的api,也但願對你們有所幫助,可以幫你買解決往後項目中可能遇到的問題。
歡迎你們訂閱個人公衆號,第一時間看到個人原創技術分享~