亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

深入講解xhr(XMLHttpRequest)/jsonp請求之abort

2019-11-19 15:59:07
字體:
來源:轉載
供稿:網友

前言

相信大家在工作中經常需要使用AJAX,所以當大家看到文章標題的時候可能會覺得這是一個老生常談的話題。

前端開發中向后端發起xhr(XMLHttpRequest)請求(代表性的就是熟悉的ajax)是再正常不過的事。

但在前端開發過程中,不怎么重視xhr的abort(中止掉xhr請求,及表示取消本次請求)。往往會帶來一些不可意料的結果。

比如:切換tab,發起xhr請求,渲染同一個列表。就這么簡單的拉取數據渲染列表的功能,并且可以根據tab切換。想想應該是很簡單。但是假如你只顧著發起xhr請求,而沒有abort掉它,想想會發生什么。很有可能就是當前選中的tab數據,并不是你想要的。說白了就是數據錯了。這時候你可能就要考慮是不是xhr請求返回數據的順序問題。

答案是肯定的,xhr請求返回數據順序是不固定的。所以你要做的就是abort掉你之前的xhr請求,然后再發起一個新的xhr請求。

結合上面所說的例子可以知道xhr使用不當會存在以下問題:

  • 容易出現頁面最終數據與狀態不一致的問題,這可能再列表篩選是出現的概率比較大。
  • xhr請求達到一定數量之后,瀏覽器就會顯得非常的慢。因為有太多的請求在請求服務器資源。

為了解決上面的問題,我們在進行頁面的時候就必須考慮abort掉所有的xhr請求。

那么如何實現xhr的abort方法呢,或者通過何種方式abort掉xhr呢?

一個簡單的xhr

我們都知道,現在的框架(例如:jQuery的ajax模塊)對xhr都進行了封裝,是為了讓我們更好的使用xhr。但是也蒙蔽了我們的眼睛。讓我們拋開框架,來看看一個簡單的xhr怎么實現。

//僅供參考 xhrfunction ajax(type ,url , data , successCallBack , errorCallBack){ let xhr = new XMLHttpRequest(); xhr.onload = ()=>{ if(xhr.status === 200){  return successCallBack(xhr.response||xhr.responseText); } return errorCallBack('請求失敗'); } xhr.onerror = ()=>{ return errorCallBack('出錯了'); } xhr.open(type,url); xhr.send(data ? data:null);}

這就是一個簡單的xhr請求的實現,我把它命名為ajax,我們現在可以通過以下方式進行調用:

ajax('get','/test/getUserList' , undefined , function(result){ console.log('成功了。', result);} ,function(error){ console.log(error);});

如果使用這個方法我們是沒辦法abort掉xhr請求的。好吧,現在我們把它改造一下,讓它支持abort方法:

//僅供參考 xhr.abortfunction ajax(type ,url , data , successCallBack , errorCallBack){ let xhr = new XMLHttpRequest(); xhr.onload = ()=>{ if(xhr.status === 200){  return successCallBack(xhr.response||xhr.responseText); } return errorCallBack('請求失敗'); } xhr.onerror = ()=>{ return errorCallBack('出錯了'); } xhr.open(type,url); xhr.send(data ? data:null); return xhr;//返回XMLHttpRequest實例對象}

好像沒有什么變化對吧。不錯,只要在函數的末尾添加return xhr;將XMLHttpRequest實例對象返回即可。那我們在就已經可以如愿的abort掉xhr請求。

let xhr = ajax('get','/test/getUserList' , undefined , function(result){ console.log('成功了。', result);} ,function(error){ console.log(error);});//abortxhr.abort();

好像我們已經大功告成了。但是問題來了,現在Promise這么好用,為什么不把它加進來呢。像這樣沒法在我們的Promise鏈式調用上使用它。

Promise封裝xhr

好了,現在的首要任務是封裝出一個Promise版的ajax庫。首要要確認的就是,ajax方法需要返回的是Promise實例對象,而不再是原生的XMLHttpRequest實例對象。知道了這一點那就可以進行封裝了。

//僅供參考 promisefunction ajax(type ,url , data ){ let xhr = new XMLHttpRequest(); let promise = new Promise(function(resolve , reject){ xhr.onload = ()=>{  if(xhr.status === 200){  return resolve(xhr.response||xhr.responseText);  }  return reject('請求失敗'); } xhr.onerror = ()=>{  return reject('出錯了'); } xhr.open(type,url); xhr.send(data ? data:null); }); return promise;//返回Promise實例對象}

使用了Promise之后我們不再需要傳入回調函數。所以參數減少了。這樣我們就可以愉快的進行鏈式調用了。

let promise = ajax('get','/test/getUserList');promise.then((result)=>{ console.log('成功了。', result);},(error)=>{ console.log(error);})

可問題又來了,Promise實例是沒有abort方法的。假如我們把ajax方法修改為返回xhr,我們是可以如期調用abort方法殺死請求,但是我們就不能使用Promise帶給我們的好處了。

仔細思考,最后一句return promise; 這里是不能改。我們只能另外想辦法。

最簡單的解決方式就是創建一個xhr和promise的映射關系。也就是每一個promise對應一個唯一的xhr請求。有了思路之后,解決方案就來了。

let map = [];//用于保存promise和xhr之間的映射關系//僅供參考 promise abortfunction ajax(type ,url , data ){ let xhr = new XMLHttpRequest(); let promise = new Promise(function(resolve , reject){ xhr.onload = ()=>{  if(xhr.status === 200){  return resolve(xhr.response||xhr.responseText);  }  return reject('請求失敗'); } xhr.onerror = ()=>{  return reject('出錯了'); } xhr.open(type,url); xhr.send(data ? data:null); }); map.push({promise:promise,request:xhr});//創建promise和xhr之間的映射關系,保存到全局的一個數組中。 return promise;//返回Promise實例對象}//abort 請求function abort(promise){ for(let i = 0 ; i < map.length ; i++ ){ if ( map[i].promise === promise ){  map[i].request.abort(); } }}

通過在全局創建一個map保存所有的promise和xhr之間的映射關系。這樣我們就可以在需要abort請求的時候根據映射關系找到xhr并abort請求。

let promise = ajax('get','/test/getUserList');promise.then((result)=>{ console.log('成功了。', result);},(error)=>{ console.log(error);})abort(promise);

好吧,到這里Promise版的ajax,我們已經實現了。是不是很簡單啊。

何為jsonp

假如你還不明白jsonp是何物,那希望下面的篇幅能讓你明白??赡苣懔阈堑闹揽缭秸埱?,但是可能沒有在實戰中碰到過。那么我們先來看看,一個簡單的jsonp函數是怎么實現的吧。

let index = 0;//僅供參考 jsonpfunction jsonp(url,jsonp,successCallback , errorCallback){ let script = document.createElement('script'); let result ; script.onload = function(){ successCallback(result); } script.onerror = function(){ errorCallback('出錯了'); } let callBackName = 'jsonpCallback'+index++; script.src=url+(url.indexOf('?') >=0 ? '&':'?')+jsonp+'='+callBackName; window[callBackName]=function(){//拿給后端進行輸出執行的。 result = Array.prototype.slice.call(arguments); } document.head.append(script);}

jsonp算起來應該就是通過script加載實現的跨域請求。其中重要的就是數據返回的接收,我們需要和后端開發同學協商回調函數的變量名。然后后端獲取到回調函數名,并且在返回時把回調函數和數據拼接成字符串返回到前端。前端我們添加一個window對象的函數用于接收數據,在函數執行完成后,就會觸發script.onload事件,這樣就可以真正執行用戶回調函數了。

可能你會覺得有點繞,其實細細的理一下,應該就明白了。

后端其實很簡單,只要獲取到jsonp函數變量名就可以了。然后把函數和數據拼接成字符串返回即可。

下面我們來看看Node.js中的實現:

let query = ctx.request.query;let jsonp = query.jsonp;//與后端協商的回調參數ctx.body = jsonp+'({code:0,msg:"success"})';

這個回調函數并不是用戶輸入的successCallback,而是jsonp函數內部的window[callBackName] ,為什么要這樣。你細想一下JavaScript的作用域應該就會知道。這就好比你在script標簽中執行一個函數一樣。

有可能我們第一次調用jsonp函數服務器會返回如下結果:

<script > //只有這一行是服務器返回的, //script標簽是document.head.append(script)時候加的 jsonpCallback0({code:0,msg:"success"});</script>

所以,得出結論就是:函數必須能通過window對象上訪問到。不然執行時就會報錯。這就是為什么我們不能直接把用戶傳入的回調直接用來當成回調接收數據的真正原因。

再次強調:JavaScript作用域。

一次成功的jsonp應該是:添加script標簽到head,后端接收到jsonp數據,返回拼接好的函數名和數據字符串,執行window對象上的函數拿到數據,執行script.onload事件,執行成功回調。

jsonp的abort方法何去何從

現在你已經知道了jsonp的原理了。那么如何才能對script加載數據進行abort呢。

犯難的問題來了,script并沒有真正的abort方法給我們使用。我們所做的就是盡最大的努力提供類似于abort功能的方法。

思路就是使用Event事件對象。觸發script的error監聽事件。所以我們得對jsonp函數添加一個trigger輔助函數進行觸發error事件。

//[trigger 觸發事件]function trigger(element,event){ if( !isString(event) ) { return; } if ( element.dispatchEvent ){ let evt = document.createEvent('Events');// initEvent接受3個參數 evt.initEvent(event, true, true); element.dispatchEvent(evt); }else if ( element.fireEvent ){ //IE element.fireEvent('on'+event); }else{ element['on'+event](); }}let index = 0;//僅供參考 jsonp.abortfunction jsonp(url,jsonp,successCallback , errorCallback){ let script = document.createElement('script'); let result ; script.onload = function(){ successCallback(result); } script.onerror = function(){ errorCallback('出錯了'); } let callBackName = 'jsonpCallback'+index++; script.src=url+(url.indexOf('?') >=0 ? '&':'?')+jsonp+'='+callBackName; window[callBackName]=function(){//拿給后端進行輸出執行的。 result = Array.prototype.slice.call(arguments); } script.abort = ()=>{ return trigger(script,'error'); }; document.head.append(script); return script;}

我們把Promise也使用進來,那樣的話,我們就可以脫離回調地獄了不是嗎?

let index = 0;//僅供參考 jsonp.abortfunction jsonp(url,query,jsonp){ let script = document.createElement('script'); let result ; let promise = new Promise(function(resolve,reject){ script.onload = function(){  return resolve(result); } script.onerror = function(){  return reject('出錯了'); } let callBackName = 'jsonpCallback'+index++; script.src=url+(url.indexOf('?') >=0 ? '&':'?')+jsonp+'='+callBackName; window[callBackName]=function(){//拿給后端進行輸出執行的。  result = Array.prototype.slice.call(arguments); } document.head.append(script); }); script.abort = ()=>{ return trigger(script,'error'); }; map.push({promise:promise,request:script});//創建promise和script之間的映射關系,保存到全局的一個數組中。 return promise;}

同樣的我們套用上面的xhr的abort函數封裝。這樣我們就大功告成了?;镜墓δ芪覀兙腿繉崿F了。我們就可以開始進行調用了。

let promise = jsonp('/test/getUserList','jsonp');promise.then((result)=>{ console.log('成功了。', result);},(error)=>{ console.log(error);})abort(promise);

總結

雖然,我們已經完成了封裝,但是還有很多的意外沒有考慮,要想再實戰中運用還必須進行封裝和重構。我們必須重視abort方法在xhr/jsonp中的運用,但是也不能濫用,適可而止。存在多層服務器調用時,應該更需要慎重考慮。

要想了解更多,可以參考這是我封裝好的一個Promise版本的ajax/jsonp庫https://github.com/Yi-love/xhrp,大家也可以通過本地進行下載。

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩毛片在线观看| 精品亚洲永久免费精品| 亚洲成人在线视频播放| 日韩精品有码在线观看| 国产精品久久久精品| 91情侣偷在线精品国产| 日韩大陆欧美高清视频区| 成人精品视频在线| 亚洲一区二区三区在线免费观看| 精品视频在线播放| 日韩欧美国产骚| 午夜精品一区二区三区在线播放| 国产成人精品国内自产拍免费看| 亚洲成人av片| 国产精品7m视频| 中国china体内裑精亚洲片| 岛国av一区二区三区| 亚洲第一男人天堂| 91禁国产网站| 中文字幕日韩av电影| 欧洲亚洲免费视频| 欧美性jizz18性欧美| 欧美xxxx综合视频| 久久久久久91| 热久久这里只有| 欧美孕妇性xx| 欧洲一区二区视频| 日韩精品高清在线| 亚洲一区中文字幕在线观看| 精品久久久久久久大神国产| 久久九九亚洲综合| 性色av一区二区三区红粉影视| 国产91在线视频| 成人两性免费视频| 欧美激情一区二区三区在线视频观看| 亚洲v日韩v综合v精品v| 国产精品福利久久久| 国产在线拍偷自揄拍精品| 精品久久久久久| 久久精品99国产精品酒店日本| 色综合视频网站| 欧美视频二区36p| 中文字幕亚洲图片| 日韩福利伦理影院免费| 日韩欧美精品免费在线| 国产欧美精品久久久| 精品少妇v888av| 亚洲天堂网在线观看| 欧美日韩美女在线观看| 欧美视频免费在线| 国产综合福利在线| 精品国产乱码久久久久久天美| 国产精品一区二区在线| 久久久久久亚洲精品中文字幕| 欧美日本中文字幕| 欧美在线观看一区二区三区| 中文字幕av一区二区三区谷原希美| 国产亚洲人成a一在线v站| 国产精品看片资源| 国产日本欧美在线观看| 96sao精品视频在线观看| 日韩精品免费观看| 国产精品久久一区主播| 成人在线激情视频| 亚洲人成在线播放| 亚洲国产美女精品久久久久∴| 日韩亚洲在线观看| 欧美日韩激情网| 久久久人成影片一区二区三区观看| 日韩欧美亚洲综合| 57pao国产精品一区| 精品无人区乱码1区2区3区在线| 午夜精品美女自拍福到在线| 色七七影院综合| 国产精品视频yy9099| 国产福利精品av综合导导航| 久久中文字幕在线视频| 在线a欧美视频| 国产又爽又黄的激情精品视频| 欧美极品少妇xxxxⅹ喷水| 深夜精品寂寞黄网站在线观看| 成人激情在线观看| 亚洲风情亚aⅴ在线发布| 亚洲人精选亚洲人成在线| 韩国欧美亚洲国产| 国产在线精品一区免费香蕉| 国产精品高潮呻吟久久av野狼| 成人乱人伦精品视频在线观看| 中文字幕亚洲自拍| 亚洲最新av网址| 欧美日韩精品在线观看| 精品亚洲精品福利线在观看| 国产999在线观看| 欧美不卡视频一区发布| 精品久久香蕉国产线看观看gif| 国产精品久久久久久久av大片| 久久好看免费视频| 91精品91久久久久久| 日韩成人av在线播放| 久久久久久久亚洲精品| 一区二区在线免费视频| 日韩影视在线观看| 国产精品一区二区av影院萌芽| 国产男女猛烈无遮挡91| 777午夜精品福利在线观看| 精品色蜜蜜精品视频在线观看| 国产激情视频一区| 在线播放国产一区中文字幕剧情欧美| 国产精品毛片a∨一区二区三区|国| 永久免费精品影视网站| 日本一本a高清免费不卡| 欧美怡红院视频一区二区三区| 成人有码在线播放| 亚洲天堂网在线观看| 日韩女优在线播放| 欧美香蕉大胸在线视频观看| 欧美一乱一性一交一视频| 欧美国产亚洲精品久久久8v| 日韩一级黄色av| 亚洲最大福利视频网| 国产国产精品人在线视| 91在线直播亚洲| 亚洲一区二区自拍| 97视频在线免费观看| 91精品国产91久久久| 国产精品视频yy9099| 久久视频在线看| 国产精品入口免费视频一| 精品久久久在线观看| 欧美激情精品久久久久久黑人| 78m国产成人精品视频| 久久久噜噜噜久久| 日本电影亚洲天堂| 超薄丝袜一区二区| 日韩在线视频二区| 国产色视频一区| 91黑丝在线观看| 久久免费在线观看| 欧美日韩激情小视频| 久久天堂av综合合色| 国产美女精品视频| 亚洲免费电影在线观看| 亚洲电影免费观看高清完整版在线| 欧美日韩国产精品一区| 91高潮精品免费porn| 国产精品久久中文| 国产精品久久久久久久久久三级| 国产欧美精品一区二区三区-老狼| 精品国产户外野外| 国产一区二区动漫| 国产精品黄色影片导航在线观看| 欧美激情久久久久久| 欧美日韩另类在线| 亚洲成人免费网站| 亚洲精品日韩av| 91久久久久久久久久久| 国产精品香蕉av| 欧美理论电影网| 九九九久久国产免费| 在线观看亚洲区| 九九精品在线视频| 精品久久国产精品| 亚洲免费人成在线视频观看| 亚洲国产高清自拍|