非同步請求與中斷 ( XHR,Axios,Fetch對比 )

少年工藤發表於2022-04-25

 隨著AJAX技術的誕生,前端正式進入了區域性重新整理和前後端分離的新時代,最初的服務請求技術是XHR,隨著技術發展和ES6的誕生,jquery ajax,axios,fetch 等技術的產生讓前端的非同步請求更便捷.

當我們使用非同步請求的時候可能會有中斷請求的需要.

                                    

比如當我們第一次查詢資料的時候沒有輸入查詢條件導致查詢很慢,於是我們第二次新增了查詢調價重新查詢很快結果返回並渲染到了頁面,

這時第一次的請求還在進行中,無法停止

                                

當我們正在看資料的時候第一次的請求返回了結果並重新渲染了頁面,導致資料混亂

 

各種請求技術怎麼又該怎麼實現呢?下邊來分別進行簡述:

 一、XHR

1.說明

AJAX 使用的 XMLHttpRequest 的物件與伺服器通訊.讓我們通過下面顯示的影像瞭解 AJAX 的流程或 AJAX 的工作原理。

2.呼叫和中斷

const xhr = new XMLHttpRequest();
const method = 'GET';
const url = 'https://xxx';
xhr.open(method, url, true);
xhr.onreadystatechange = () => {
  if (xhr.readyState === 4) {
    // do something
  }
}
xhr.send();

setTimeout(()=>{
    xhr.abort()}
,1000)   

jquery Ajax由於也是相同的

var ajaxGet = $.get(“https://xxx”,
  {id:
1},
 
function(data){undefined ….//一些操作 }
);
ajaxGet.abort();

二、axios

1.說明

眾所周知xhr技術雖然實現了非同步呼叫但是如果連續有序地呼叫多個請求就會出現回撥地獄的尷尬場面.

ES6推出的async/await promise可以很好的解決這個問題.而axios就是基於promise對xhr進行的封裝

核心程式碼如下(簡單模擬非原始碼):

非同步請求與中斷 ( XHR,Axios,Fetch對比 )
 1  function axios(config){
 2         return new Promise((resolve) => {
 3             const {url='',data={},method='get'} = config; //解構傳參
 4             const xhr = new XMLHttpRequest;     //建立請求物件
 5             xhr.open(method,url,true); 
 6             xhr.onreadystatechange = () => {
 7                 if(xhr.readyState == 4 && xhr.status == 200){
 8                     resolve(xhr.responseText);
 9                     //非同步請求返回後將Promise轉為成功態並將結果匯出
10                 }
11             }
12             xhr.onerror = (err) => {
13                 reject(err);
14             };
15             xhr.send(JSON.stringfy(data));
16         })
17     }
View Code

2.使用

 // then catch 鏈式呼叫
   axios.get('/user')
   .then(function (response) {
     console.log(response);
     axios.get('class?info=' + response.data.name);
    })
   .catch(function (error) {
      console.log(error);
    });

 // async await 
   var info = await axios.get('user');
   var ret =  await axios.get('class?info=' + info.data.name);

 3.中斷(取消)

axios 的中斷取消是基於 cancelable promises proposal

原理是內部生成一個Promise 將 resove 方法拋給外部的 source的cancel方法,

當外部呼叫這個方法時,內部的promise.then就會呼叫xhr.abort() 並呼叫外部的reject

 

可以使用 CancelToken.source 工廠方法建立 cancel token,像這樣:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
   // 取消處理 console.log(
'Request canceled', thrown.message); } else { // 處理錯誤 } }); axios.post('/user/12345', { name: 'new name' }, { cancelToken: source.token }) // 取消請求(message 引數是可選的) source.cancel('Operation canceled by the user.');

還可以通過傳遞一個 executor 函式到 CancelToken 的建構函式來建立 cancel token:

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函式接收一個 cancel 函式作為引數
    cancel = c;
  })
});

// cancel the request
cancel();

 

三、Fetch

 1.說明

Fetch也是基於ES6 Promise 實現的一個伺服器請求技術,但不是對xhr的封裝.

也是底層的實現不需要引入包,是 XMLHttpRequest 的升級版.相容除了IE的大部分瀏覽器

2.基本使用

 // then  catch 鏈式呼叫
fetch('https://xxxx')
.then(response => response.json())
.then(json => console.log(json))
.catch(err => console.log('Request Failed', err));

// async await
async function getJSON() {
let url = 'https:XXXX';
try {
let response = await fetch(url);
return await response.json();
} catch (error) {
console.log('Request Failed', error);
}
}

3.中斷

Fetch的中斷是基於webApi的 AbortController(實驗階段的功能相容除了IE的大部分瀏覽器)

var controller = new AbortController();
var signal = controller.signal;

// 可以監聽取消事件
signal.addEventListener('abort',
  () => console.log('abort!')
);

setTimeout(()=>{
    //定時或者手動呼叫abort方法中斷
     controller.abort();
  },1000)

 fetch('http://xxxx', {signal}).then(function(response) {
    ...
  }).catch(function(e) {
    if(signal.aborted){
     // 可以通過 signal.aborted 屬性判斷
      ...
    }
    if(e.name=='AbortError'){
     // 也可以通過 error.name 判斷
     ...
    }
  })/

四 、其他實現方法

 其實在知道這些中斷方法之前本人還用過其他的方法——uuid

 主要思路就是每次呼叫請求的時候生成一個uuid,將這個uuid賦值給全域性的變數同時作為引數傳給請求的方法.

 在請求返回處理資料的時候驗證當前的全域性uuid 是否和當前呼叫引數是否一致,不一致就不渲染資料,

 這樣就能保證渲染的資料是最後一次呼叫請求的資料

//以Fetch為例

this.uuid = ""

 // 自己寫一個生成uuid的方法,或者使用第三方的包
function genUUID(){
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = (Math.random() * 16) | 0
        var v = c === 'x' ? r : (r & 0x3) | 0x8
        return v.toString(16)
    })
}

function fetchData(){
  // 賦值給區域性變數和全域性變數
  let  uuid = genUUID()
  this.uuid = uuid
  fetch(url).then(res=>{
     if(this.uuid === uuid){
      // 渲染資料
     }
   })

}

 

 

參考連結:
https://www.w3cschool.cn/ajax/ajax-tutorial.html
https://www.cnblogs.com/ysk123/p/11544211.html

相關文章