多次请求只保持最后一次的结果

本文探讨了优化前端项目中异步搜索请求的两种策略:一是利用函数作用域内的标记值判断最后一次请求,二是借助全局时间戳并与后台配合。作者强调了第一种方法的适用性和第二种方法的局限性。

最近项目总结,如果一个输入框和一个搜索按钮,点击查询的时候,需要发起请求获取数据,因为请求是异步的,则无法获取哪一次的请求是最后一次的请求,则可以进行一下操作优化:

思路一:请求外部存在一个变量,记录每次请求的标记值;可以将外部的标记值传入调用请求的内部,因为函数有作用域的概念,所以暂时目前传入的数据是会一直存在于该作用域内部的,但是外部的标记值会一直变化,当是最后一次请求的时候,外部的值和内部接收的值是相同的,可以在此处进行数据处理。

vue代码如下:

<template>
  <div class="root">
    <input type="text" v-model="inputData" />
    <button @click="handleSearch">搜搜</button>
    <ul v-for="item in appList" :key="item.value">
      <li>{{ item.label }}</li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      inputData: "",
      appList: [],
      time: "",
    };
  },
  methods: {
    handleSearch() {
      this.time = new Date().getTime(); //主要是这里,记录当前点击的时间戳
      this.sendRequestFunc(this.time);
    },
    sendRequestFunc(timer) {
      this.sendRequest("/microx-user/admin/org-info/all",{content:this.inputData}).then((res) => {
        if (timer == this.time) {
          //因为函数有作用域的概念,所以上一次点击的时候会开辟一个新的空间,保留上一次传入的数据,但是外部的数据会实时发生变化
          console.log(
            "有响应之后的最后一次数据处理可在这里进行",timer, this.time
          );
          /* 
              此处进行数据处理
          */

          this.appList = [];
          if (res.returnCode == 0) {
            res.body.forEach((item) => {
              var obj = {
                label: item.orgName,
                value: item.orgId,
              };
              this.appList.push(obj);
            });
          } else {
            this.$message.error(res.returnMsg);
          }
        } else {
          console.log("证明不是最后一次请求", timer, this.time);
        }
      });
    },
  },
};
</script>
<style scoped>
.root {
  width: 1000px;
  height: 1000px;
  background: cyan;
}
</style>

弱网环境模拟结果展示:

 

 值得注意的是:这里一定是上一次请求没有回来,才会进行这种处理操作!!

基于这个思路,可以使用其他的标记值,如内部自定义1,2,3,4,5,……,或者哈希值等等,但是最好还是用时间戳,因为时间戳可以看一下大小值,也算是一个好处吧

思路二:

每次请求需要携带一个标记值,假设取用的是时间戳,然后全局存储时间戳,需要协调后台对此标记值返回,此时比较全局变量和响应中的标记值,即可判断是否是最后一次的请求结果了。

 大概思路就是这样,但是这种方法需要后台支持,并且返回的标记值即是请求的请求数据之一,所以大概率会被拒绝这样的需求,所以还是建议第一种方法。

完结!!!

<think>在Vue3中,我们经常遇到连续多次触发同一个异步操作(如接口调用)的情况,而我们希望只保留最后一次调用的结果,忽略之前所有调用的结果。这通常发生在用户连续输入触发搜索、频繁点击等场景。根据用户需求,结合站内引用和之前的回答,以下是几种实现方式:###方法1:使用标识变量(推荐简单场景)在组件内部,我们可以使用一个标识变量(如`currentRequestId`)来记录当前最新的请求ID。每次发起新请求时,更新这个ID,并在请求返回时检查是否与当前最新ID一致。```vue<scriptsetup>import{ref}from'vue';letcurrentRequestId=0;constdata=ref(null);asyncfunctionfetchData(){//每次调用生成新的IDconstthisRequestId=++currentRequestId;try{constresponse=awaitfetch('https://api.example.com/data');constresult=awaitresponse.json();//只有最近一次请求结果会被保留if(thisRequestId===currentRequestId){data.value=result;}}catch(error){//同样需要检查IDif(thisRequestId===currentRequestId){console.error('Fetcherror:',error);}}}</script>```###方法2:使用AbortController取消之前的请求(推荐)通过`AbortController`可以主动取消之前的请求,确保只有最后一次请求有效。```vue<scriptsetup>import{ref}from'vue';letabortController=null;constdata=ref(null);asyncfunctionfetchData(){//如果存在之前的请求,则取消if(abortController){abortController.abort();}//创建新的AbortControllerabortController=newAbortController();try{constresponse=awaitfetch('https://api.example.com/data',{signal:abortController.signal//传入信号});constresult=awaitresponse.json();data.value=result;}catch(error){//如果是主动取消的请求,不需要处理if(error.name!=='AbortError'){console.error('Fetcherror:',error);}}finally{//清空abortController引用abortController=null;}}</script>```###方法3:封装为可复用的Composable(推荐)我们可以将上述逻辑封装成一个Vue3的Composable函数,以便在多个组件中复用。```javascript//useLastedAsync.jsimport{ref}from'vue';exportfunctionuseLastedAsync(asyncFn){constdata=ref(null);consterror=ref(null);letcurrentRequestId=0;constexecute=(...args)=>{constthisRequestId=++currentRequestId;returnasyncFn(...args).then(result=>{if(thisRequestId===currentRequestId){data.value=result;error.value=null;}returnresult;}).catch(err=>{if(thisRequestId===currentRequestId){error.value=err;}throwerr;});};return{data,error,execute};}```在组件中使用:```vue<scriptsetup>import{useLastedAsync}from'./useLastedAsync';const{data,error,execute}=useLastedAsync((query)=>fetch(`https://api.example.com/search?q=${query}`).then(res=>res.json()));//在需要的地方调用execute,比如输入框的input事件consthandleInput=(e)=>{execute(e.target.value);};</script>```###方法4:使用axios的取消令牌(如果使用axios)如果项目中使用了axios,可以利用axios的取消令牌(CancelToken)来实现[^2]。```javascriptimportaxiosfrom'axios';//创建一个取消令牌源constCancelToken=axios.CancelToken;letcancel;functionfetchData(){//如果之前有请求,取消它if(cancel)cancel('Operationcanceledduetonewrequest.');//发起新请求axios.get('https://api.example.com/data',{cancelToken:newCancelToken(functionexecutor(c){//保存取消函数cancel=c;})}).then(response=>{//处理响应}).catch(error=>{if(axios.isCancel(error)){console.log('Requestcanceled',error.message);}else{//处理其他错误}});}```###总结-**简单场景**:使用方法1(标识变量)或方法2(AbortController)即可。-**复用需求**:封装成Composable(方法3)是最佳选择。-**使用axios**:直接使用其提供的取消令牌机制(方法4)。这些方法都能有效确保只保留最后一次异步调用的结果,提升用户体验并减少不必要的网络请求。---相关问题:1.Vue3中如何优雅地处理多个并发请求的竞态问题?2.如何封装一个通用的Vue3Composable函数来处理异步请求?3.Vue3项目中,如何结合使用AbortController和axios?4.Vue3的CompositionAPI中如何处理异步操作的副作用?5.如何测试Vue3组件中涉及异步请求取消逻辑的行为?</think>在 Vue3 中处理多次接口调用只保留最后一次返回结果的场景(如搜索建议、频繁操作按钮),可以通过以下几种方式实现: ### 方法一:使用请求标识符(推荐简单场景) ```vue <script setup> import { ref } from 'vue'; let requestId = 0; const results = ref(null); async function fetchData() { const currentId = ++requestId; // 创建唯一标识 const response = await fetch('/api/data'); const data = await response.json(); // 只处理最后一次请求结果 if (currentId === requestId) { results.value = data; } } </script> ``` ### 方法二:使用 AbortController(推荐现代浏览器) ```vue <script setup> import { ref } from 'vue'; let abortController = null; const results = ref(null); async function fetchData() { // 取消前一个请求 if (abortController) { abortController.abort(); } // 创建新的控制器 abortController = new AbortController(); try { const response = await fetch('/api/data', { signal: abortController.signal }); const data = await response.json(); results.value = data; } catch (error) { if (error.name !== 'AbortError') { console.error('请求错误:', error); } } } </script> ``` ### 方法三:封装可复用的 composable(推荐复杂应用) ```javascript // useLastCall.js import { ref } from 'vue'; export function useLastCall(asyncFn) { const data = ref(null); const error = ref(null); let currentId = 0; const execute = async (...args) => { const thisId = ++currentId; try { const result = await asyncFn(...args); if (thisId === currentId) { data.value = result; error.value = null; } return result; } catch (err) { if (thisId === currentId) { error.value = err; } throw err; } }; return { data, error, execute }; } ``` 在组件中使用: ```vue <script setup> import { useLastCall } from './useLastCall'; const { data, execute } = useLastCall(async (query) => { const response = await fetch(`/api/search?q=${query}`); return response.json(); }); // 调用示例 const handleInput = (e) => { execute(e.target.value); }; </script> ``` ### 方法四:使用 axios 取消令牌(如果使用 axios) ```javascript import axios from 'axios'; // 创建取消令牌源 const CancelToken = axios.CancelToken; let cancel; function fetchData() { // 取消前一个请求 if (cancel) cancel(); axios.get('/api/data', { cancelToken: new CancelToken(c => { cancel = c; // 保存取消函数 }) }).then(response => { results.value = response.data; }).catch(error => { if (!axios.isCancel(error)) { console.error('请求错误:', error); } }); } ``` ## 使用场景对比 | **方法** | **适用场景** | **优点** | **缺点** | |-------------------|--------------------------------|-----------------------------|----------------------| | 请求标识符 | 简单场景、少量请求 | 实现简单,无需额外库 | 无法真正取消网络请求 | | AbortController | 现代浏览器项目 | 真正取消网络请求,节省带宽 | 旧浏览器不支持 | | Composable 封装 | 大型应用,多处复用 | 高复用性,逻辑封装清晰 | 实现稍复杂 | | Axios 取消令牌 | 使用 axios 的项目 | 与 axios 深度集成 | 仅适用于 axios | ## 最佳实践建议 1. **现代浏览器项目**:优先使用 `AbortController`,它能真正取消网络请求 2. **需要复用逻辑**:封装为 composable 函数,保持代码整洁 3. **简单场景**:使用请求标识符方法足够高效 4. **错误处理**:总是处理取消请求时的特殊错误(AbortError 或 Cancel) 这些方法都能有效解决"多次调用只取最后一次结果"的问题,核心原理是:**通过标识或取消机制确保只有最新请求结果被处理**。 --- 相关问题: 1.Vue3 的 composable 中如何正确处理异步操作的副作用? 2. 如何为频繁触发的搜索输入实现防抖请求优化? 3. Vue3 中如何使用 Axios 拦截器统一处理请求取消逻辑? 4. 在组合式 API 中如何优雅地处理多个并发异步操作? 5. 如何测试包含异步请求取消逻辑的 Vue3 组件?
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值