用axios搜索:当有新字符时取消先前的请求

时间:2019-04-24 16:16:41

标签: vue.js axios

我有一个搜索栏,每个字母的结果都会更新,但是例如,当用户快速键入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;
                    }
                );
            }
        }

2 个答案:

答案 0 :(得分:2)

我能够使它工作。.诀窍是在启动API调用之前检查取消令牌是否存在。.我不得不移动CancelToken和{{1} } cancel对象/组件之外的变量。


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

Vue
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();
      }
    }
  }
});


[CodePen mirror]


已取消的请求:

enter image description here

答案 1 :(得分: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中。这一切都取决于您的其余代码和功能。