亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

 更新時間:2017年07月26日 09:35:37   作者:Jin  
這篇文章主要給大家深入的介紹了關于xhr(XMLHttpRequest)/jsonp請求之abort的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起看看吧。

前言

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

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

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

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

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

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

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

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

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

一個簡單的xhr

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

//僅供參考 xhr
function 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.abort
function 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);
});
//abort
xhr.abort();

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

Promise封裝xhr

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

//僅供參考 promise
function 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; 這里是不能改。我們只能另外想辦法。

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

let map = [];//用于保存promise和xhr之間的映射關系
//僅供參考 promise abort
function 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});//創(chuàng)建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();
 }
 }
}

通過在全局創(chuàng)建一個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是何物,那希望下面的篇幅能讓你明白??赡苣懔阈堑闹揽缭秸埱?,但是可能沒有在實戰(zhàn)中碰到過。那么我們先來看看,一個簡單的jsonp函數是怎么實現的吧。

let index = 0;
//僅供參考 jsonp
function 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(){//拿給后端進行輸出執(zhí)行的。
 result = Array.prototype.slice.call(arguments);
 }
 document.head.append(script);
}

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

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

后端其實很簡單,只要獲取到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標簽中執(zhí)行一個函數一樣。

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

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

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

再次強調:JavaScript作用域。

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

jsonp的abort方法何去何從

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

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

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

//[trigger 觸發(fā)事件]
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.abort
function 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(){//拿給后端進行輸出執(zhí)行的。
 result = Array.prototype.slice.call(arguments);
 }
 script.abort = ()=>{
 return trigger(script,'error');
 };
 document.head.append(script);
 return script;
}

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

let index = 0;
//僅供參考 jsonp.abort
function 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(){//拿給后端進行輸出執(zhí)行的。
  result = Array.prototype.slice.call(arguments);
 }
 document.head.append(script);
 });
 script.abort = ()=>{
 return trigger(script,'error');
 };
 map.push({promise:promise,request:script});//創(chuàng)建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);

總結

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

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

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

相關文章

  • 詳解CocosCreator項目結構機制

    詳解CocosCreator項目結構機制

    這篇文章主要介紹了詳解CocosCreator項目結構機制,只有了解這些機制后,才能更好的進行項目開發(fā),避免潛在錯誤,并且快速的除錯
    2021-04-04
  • 通過bootstrap全面學習less

    通過bootstrap全面學習less

    這篇文章主要為大家詳細介紹了如何通過bootstrap學習less,LESS CSS是一種動態(tài)樣式語言,屬于CSS預處理語言的一種,感興趣的朋友可以參考下
    2016-11-11
  • 鼠標懸浮停留三秒后自動顯示大圖js代碼

    鼠標懸浮停留三秒后自動顯示大圖js代碼

    這篇文章主要介紹了鼠標懸浮停留三秒后顯示大圖,在網頁中還是比較實用的,下面是示例代碼
    2014-09-09
  • js實現注冊頁面校驗功能

    js實現注冊頁面校驗功能

    這篇文章主要為大家詳細介紹了js實現注冊頁面校驗功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • JS數組方法reduce的妙用分享

    JS數組方法reduce的妙用分享

    在?JavaScript?中,reduce?是最難理解的數組方法之一,它是一個強大而靈活的高階函數,下面就來看看?reduce?的妙用之處,希望對大家有所幫助
    2023-02-02
  • js實現搜索框關鍵字智能匹配代碼

    js實現搜索框關鍵字智能匹配代碼

    這篇文章主要為大家分享了js實現搜索框關鍵字智能匹配代碼,感興趣的朋友可以參考一下
    2016-01-01
  • 詳解JavaScript的定時器

    詳解JavaScript的定時器

    這篇文章主要為大家介紹了JavaScript的定時器 ,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-11-11
  • JavaScript數據結構之數組的表示方法示例

    JavaScript數據結構之數組的表示方法示例

    這篇文章主要介紹了JavaScript數據結構之數組的表示方法,從數據結構線性表的角度分析了數組的原理并結合實例形式分析了javascript數組的定義與使用方法,需要的朋友可以參考下
    2017-04-04
  • 微信小程序使用webview頁面轉pdf文件代碼示例

    微信小程序使用webview頁面轉pdf文件代碼示例

    工作需求,將webview的內容導出到pdf輸出,下面這篇文章主要給大家介紹了關于微信小程序使用webview頁面轉pdf文件的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-09-09
  • 關于JS中的apply,call,bind的深入解析

    關于JS中的apply,call,bind的深入解析

    下面小編就為大家?guī)硪黄P于JS中的apply,call,bind的深入解析。小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-04-04

最新評論