【问题标题】:Proper setup of Vue.js methods to deal with Axios asynchronous request正确设置 Vue.js 方法来处理 Axios 异步请求
【发布时间】:2019-12-11 13:14:42
【问题描述】:

我有 Vue.js 应用程序,它使用 Axios 从 API 获取一些复杂数据,然后可视化这些数据。 我的代码与此类似:

{
  data: () => ({
    data: null,  // or Object, it doesn't matter now
    loading: false,
    errored: false,
    loadingMessage: ''
  }),
  methods: {
    loadData: function() {
      this.loading = true;
      this.loadingMessage = 'Fetching Data';
      axios.get('api/url/').then((response) => {
        this.data= response.data;  // or this.$set(this, 'data', response.data), it doesn't matter now
        this.loadingMessage = 'Process Data';
        this.processData();
      })
      .catch(function () {
        this.errored= true;
      })
      .then(function () {
        this.loading = false;
      })
    },
    processData: function() {
        // process data
    }
  }
}

然后我点击模板中的按钮,该按钮调用loadData() 函数。 它工作正常,但获取数据需要一些时间,处理也需要一些时间,并且 Vue 仅在 axios 请求完成时更改模板和变量。所以我只看到Fetching Data 消息,但没有看到Process Data

如何向用户展示现在处理数据的阶段? 也许我应该在watch methods 中调用processData() 函数,但这对我来说似乎有点过头了。

更新

我最终得到了setTimeout() 换行符。请参阅下面的答案。

【问题讨论】:

    标签: vue.js asynchronous axios


    【解决方案1】:

    Vue 有一个名为nextTick 的函数,它是一个异步函数,基本意思是“在下一次视觉更新后执行以下代码”。如果我有这样的视觉更新,我通常会使用这种方法。

    我认为您示例中的代码如下所示:

    axios
      .get('api/url/')
      .then((response) => {
        this.data= response.data;
        this.loadingMessage = 'Process Data';
        return this.$nextTick();
      })
      .then(() => {
        this.processData();
      })
      .catch (etc ...  
    

    我不完全确定。我通常在启用 webpack/babel 的环境中工作,在这种情况下,我会创建整个函数 async 并编写:

    async function fn() {
      const response = await axios.get('api/url/');
      this.data = response.data;
      this.loadingMessage = 'Process Data';
      await this.$nextTick();
      this.processData();
    }
    

    【讨论】:

    • 谢谢,我想这是我需要的。我会在几个小时内测试您的选择并返回。
    • 不幸的是,您的方法对我没有帮助。我发布了一个可以解决我的问题的答案。但老实说,我不太明白它是如何工作的。如果您有任何想法为什么会这样,如果您向我解释,我会很高兴。
    【解决方案2】:

    你可以在这里阅读它 (https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue)

    你可以尝试改变你的功能如下:

    loadData: function() {
      this.loading = true;
      this.loadingMessage = 'Fetching Data';
      axios.get('api/url/').then((response) => {
        this.data= response.data;  // or this.$set(this, 'data', response.data), it doesn't matter now
        this.$nextTick(() => {
          this.loadingMessage = 'Process Data';
          this.processData();
        })
      })
      .catch(function () {
        this.errored= true;
      })
      .then(function () {
        this.loading = false;
      })
    },
    

    【讨论】:

    • 谢谢,但不知何故这不起作用。我最终得到了setTimeout() wrap。
    【解决方案3】:

    按照@joachim-bøggild 的建议,我尝试使用$nextTick()。我什至尝试使用$forceUpdate() 来实现这个目标。但是由于某种我不清楚的原因,没有观察到我需要的效果。控制台显示该值已更改。但在请求完成之前,屏幕上和 Vue Devtools 中都会显示旧结果。

    所以我决定用一个例子来补充这个问题,并开始在 JsFiddle 上创建演示。为了显示我需要的渲染延迟,我使用了setTimeout(() =>{ this.loading = false}, 1000),完全没有问题。

    我将这种方法移植到我的代码中,一切都运行良好。直到我试图删除这个setTimeout()。问题又来了。

    因此,我最终选择了这样的代码设计:

    ...
          axios.get('api/url/').then((response) => {
            this.data= response.data;
            this.loadingMessage = 'Process Data';
            setTimeout(() => {
              this.processData();
              this.loading = false;
            }, 10);  // if this value less than 10 error still occurs
          })
          .catch(function () {
            this.errored= true;
          })
    ...
    

    如果有人理解为什么会发生这种行为,请完成我的回答。

    【讨论】: