• 作者:老汪软件技巧
  • 发表时间:2024-08-27 15:02
  • 浏览量:


var xhr = new XMLHttpRequest(),
  method = "GET",
  url = "https://developer.mozilla.org/";
xhr.open(method, url, true);
xhr.send();
if (OH_NOES_WE_NEED_TO_CANCEL_RIGHT_NOW_OR_ELSE) {
  xhr.abort();
}

如果用fetch,也有AbortController可以取消

const controller = new AbortController();
const signal = controller.signal;
const url = "video.mp4";
const downloadBtn = document.querySelector(".download");
const abortBtn = document.querySelector(".abort");
downloadBtn.addEventListener("click", fetchVideo);
abortBtn.addEventListener("click", () => {
  controller.abort();
  console.log("Download aborted");
});
function fetchVideo() {
  fetch(url, { signal })
    .then((response) => {
      console.log("Download complete", response);
    })
    .catch((err) => {
      console.error(`Download error: ${err.message}`);
    });
}

对于常用的Axios,支持以 fetch API 方式——AbortController取消请求

const controller = new AbortController();
async function getOpinionList (time) {
 
 const res = await axios.post('https://xxx', {
    signal: controller.signal
  })
 await new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, time)
  })
  return Promise.resolve(res);
}
let res;
function fetchData (name, time) {
  controller.abort();// 取消请求
  getOpinionList(name, time).then(function(response) {
    res = { name };
  });
}
fetchData('A请求', 3000);
fetchData('B请求', 2000);
fetchData('C请求', 1000);
setTimeout(() => {
  console.log(res);
}, 4000)

请求结果保存的是最后触发的C请求

3.requestId

用特殊字符当成requestId,利用axios的请求拦截器,在请求头上加上唯一标识,本地存储最后请求的唯一标识符。

const axiosInstance = axios.create();
const localStorage = {
  lastRequestId: 0,
  lastRequestName: undefined
}
let list = [];
// 添加请求拦截器
axiosInstance.interceptors.request.use(config => {
  
  // 生成一个唯一的请求ID,这里使用时间戳作为示例
  const requestId = `${Date.now()}`;
  // 将requestId添加到请求的headers中
  config.headers['X-Request-Id'] = requestId;
  config.headers['X-Request-name'] = encodeURIComponent(config.data.name);
  
  // 收集到的三次请求的时间戳是一样的,requestId不能用Date.now(),应该用UUID
  list.push(requestId);
  
  // 可以将requestId存储起来,例如在localStorage或全局状态管理中
  localStorage['lastRequestId'] = requestId;
  localStorage['lastRequestName'] = config.data.name;
  // 返回修改后的配置
  return config;
}, error => {
  // 请求错误处理
  return Promise.reject(error);
});
async function getOpinionList (name,time) {
   
  const res = await axiosInstance.post('https://xxx',{name});
  
  await new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, time)
  })
  return Promise.resolve(res);
}
let res;
function fetchData (name, time) {
  
  getOpinionList(name, time).then(function(response) {
 
    const rName = decodeURIComponent(response.config.headers['X-Request-name']);
    console.log('响应的请求', rName);
    if (rName === localStorage['lastRequestName']) {
      res = { name };
    }
   
  });
}
fetchData('A请求', 3000);
fetchData('B请求', 2000);
fetchData('C请求', 1000);
setTimeout(() => {
  console.log(localStorage)
  console.log(list)
  console.log('存储的结果',res);
}, 4000)

在测试过程中发现,如果单纯用Date.now()当成requestId,则三次请求的时间戳都是一样的,就会导致不准确,所以requestId不能单纯使用时间戳,而是用UUID的形式,测试demo是用了lastRequestName存储唯一标识。请求结果如

4.时间戳

让后端返回一个时间戳,每次都判断,保证永远只保存最后一次发起的请求的时间戳的数据即可。

function getOpinionList (time) {
 
  return axios.post('https://xxx')
}
let res;
let maxTimestamp = 0;// 记录最后一次请求的时间戳,依旧大小更新
const list = [];
function fetchData (name) {
 
  getOpinionList(name).then(function(response) {
    const timestamp = response.data.timestamp;
    list.push({ name, timestamp});
    if (timestamp > maxTimestamp) {
      
      res = { name ,timestamp};
    }
    
  });
}
fetchData('A请求', 5000);
fetchData('B请求', 3000);
fetchData('C请求', 1000);
setTimeout(() => {
  console.log('各请求时间戳',list)
  console.log('存储结果',res);
}, 6000)

结果如

总结

本文介绍了4种解决竞态条件的处理方案,其中方案一是在Vue3的watch设计中学来的,不失为一种虽然有点绕,但是极为优雅的处理方式,不依赖于第三方插件功能或者是其他的附加内容信息,单单依靠JS的闭包就能实现,相当优雅了。