用 axios 搜索:新字符时取消之前的请求

Posted

技术标签:

【中文标题】用 axios 搜索:新字符时取消之前的请求【英文标题】:Search with axios: cancel previous request when new character 【发布时间】:2019-09-14 00:19:18 【问题描述】:

我有一个搜索栏,每个字母都会更新结果,但是当用户快速输入 3 个字母时,之前的请求不会被取消,所以在他得到结果之前会有一个难看的延迟。

我已阅读此https://github.com/axios/axios#cancellation,也许我有点累,但我很难将它添加到我的项目中。它几乎起到了相反的效果,现在它需要永远。 您有什么建议或者您推荐一个好的教程以便我理解这一点吗?

<input v-model="query" type="text" class="form-control" placeholder="Runner name or description"
                   aria-label="Runner name or description"
                   aria-describedby="basic-addon2">


watch: 
            query: 
                handler: _.debounce(function () 
                    this.newSearch()
                , 100)
            
        ,
methods: 
            searchItems() 
                let filters = 
                    q: this.query
                ;

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

                axios.post('/Items/find', filters, 
                    cancelToken: source.token
                )
                .catch(function(thrown) 
                    if (axios.isCancel(thrown)) 
                        console.log('Request canceled', thrown.message);
                    
                )
                .then(
                    response => 
                        if(this.Items!=null)
                            response.data.forEach(element => 
                                this.Items.push(element);
                            );
                        
                        else
                            this.Items=response.data;
                    
                );
            ,
            newSearch()
                const CancelToken = axios.CancelToken;
                const source = CancelToken.source();
                source.cancel('Cancel previous request');

                this.countItems();
                this.searchItems();
            ,
            showMore()
                this.startindex = this.startindex+this.nbrows;
                this.searchItems();
            ,
            countItems()
                this.countItems=10;
                let filters = 
                    q: this.query
                ;
                axios.post('/Items/count', filters).then(
                    response => 
                        this.countItems=response.data;
                    
                );
            
        

【问题讨论】:

你试过增加去抖吗? 【参考方案1】:

问题是您在 newSearch 中创建了一个新的 canceltoken 并取消了它而不是原来的那个。 如果您将源代码保存在 Vue 组件上,您可以检查 newSearch(如果存在),然后再取消。 发布 Promise 完成后,您将再次删除源,以便在不需要(或可能)时无法取消。


  searchItems() 
      let filters = 
          q: this.query
      ;

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

      axios.post('/Items/find', filters, 
          cancelToken: this.searchItemsSource.token
      )
      .catch(function(thrown) 
          if (axios.isCancel(thrown)) 
              console.log('Request canceled', thrown.message);
          
      )
      .then(
          response => 
              this.searchItemsSource = undefined;
              if(this.Items!=null)
                  response.data.forEach(element => 
                      this.Items.push(element);
                  );
              
              else
                  this.Items=response.data;
          
      );
  ,
  newSearch()
      if (this.searchItemsSource) 
          this.searchItemsSource.cancel('Cancel previous request');
      
      this.countItems();
      this.searchItems();
  ,

关于此代码的附注;我实际上会将取消上一个请求移到 searchItems 方法中,因为同时有两个正在运行的调用是没有意义的。它看起来更像这样:


  searchItems() 
      let filters = 
          q: this.query
      ;

      if (this.searchItemsSource) 
          this.searchItemsSource.cancel('Cancel previous request');
      
      const CancelToken = axios.CancelToken;
      this.searchItemsSource = CancelToken.source();

      axios.post('/Items/find', filters, 
          cancelToken: this.searchItemsSource.token
      )
      .catch(function(thrown) 
          if (axios.isCancel(thrown)) 
              console.log('Request canceled', thrown.message);
          
      )
      .then(
          response => 
              this.searchItemsSource = undefined;
              if(this.Items!=null)
                  response.data.forEach(element => 
                      this.Items.push(element);
                  );
              
              else
                  this.Items=response.data;
          
      );
  ,
  newSearch()
      this.countItems();
      this.searchItems();
  ,

此时您可以问问自己是否需要 newSearch 方法,您可以删除它并将 countItems 调用也移到 searchItems 中。不过,这一切都取决于您的其余代码和功能。

【讨论】:

非常感谢这个完整的答案,事实上我也只是将 countitems 移到了 searchItems 中。更容易阅读:)【参考方案2】:

我能够让它工作。诀窍是在启动 API 调用之前检查取消令牌是否存在。我不得不将 CancelTokencancel 变量移到外面Vue 对象/组件..


此示例在 GitHub 上搜索存储库...

var cancel;
var CancelToken = axios.CancelToken;

new Vue(
  el: "#app",
  data: 
    query: "",
    results: "",
    isLoading: false
  ,
  methods: 
    clear() 
      this.isLoading = false;
      this.results = "";
      this.query = "";
    ,

    handleSearch: _.debounce(function() 
      this.preApiCall();
    , 300),

    preApiCall() 
      if (cancel != undefined) 
        cancel();
        console.log("cancelled");
      
      this.apiCall(this.query);
    ,

    apiCall(query) 
      if (query !== "") 
        this.isLoading = true;
        axios(
          method: "get",
          url: "https://api.github.com/search/repositories",
          cancelToken: new CancelToken(function executor(c) 
            cancel = c;
          ),
          params: 
            q: query
          
        ).then(res => 
          this.results = JSON.parse(JSON.stringify(res.data.items));
          this.isLoading = false;
        ).catch(err => 
          this.results = err.message;
          throw Error(err.message);
          this.isLoading = false;
        );
       else 
        this.clear();
      
    
  
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

<div id="app">
  <input v-model="query" @keyup.stop="handleSearch" type="text" class="form-control" placeholder="Search">
  <button @click.stop="clear">Clear</button>
  <div v-if="isLoading">Loading...</div>
  <ul v-if="results !== ''">
    <li v-for="(r, index) in results" :key="index">
       r.name 
    </li>
  </ul>
</div>

[CodePen mirror]


已取消的请求:

【讨论】:

以上是关于用 axios 搜索:新字符时取消之前的请求的主要内容,如果未能解决你的问题,请参考以下文章

axios取消功能的设计与实现

Axios 如何取消已发送的请求?

vue axios请求频繁时取消上一次请求

vue路由变化时使用axios取消所有请求

反应搜索 onChange 取消请求

vue axios拦截器常用之重复请求取消