Night
Night

Reputation: 85

Cannot find a way to wait for vue.js async function

I have the following code that works when I uncomment the "return" in fetchData below. How can I set it up so I wait for this.fetchData to finish to fill items? I have tried variants of other promises, async, await, but cannot get it to actually work...

So far I set a this.items inside fetchData but I realize it's absolutely not what I want to actually do. It shows me the first X lines of my call, because of setting it this way, but all the code that is supposed to happen after the fetchData call is bugging, starting with trying to do items.length.

import axios from 'axios';

new Vue({
    el: '#app',
    data() {
        return {
            search: '',
            totalItems: 0,
            items: [],
            loading: true,
            pagination: {},
            headers: [ //some headers
            ],
            items: []
        }
    },
    watch: {
      pagination: {
        handler () {
          this.getDataFromApi()
            .then(data => {
              this.items = data.items
              this.totalItems = data.total
            })
        },
        deep: true
      }
    },
    mounted () {
      this.getDataFromApi()
        .then(data => {
          this.items = data.items
          this.totalItems = data.total
        })
    },
    methods: {
        fetchData() {
            axios.get('/api/v1/translations').then(response => {
                //console.log(response.data)
                this.items = response.data.data
            })

            //the return below is working 100%:
            //return [{id:1,key:1,lg:'en',text:'woopie'}];
        },

        getDataFromApi () {
            this.loading = true
            return new Promise((resolve, reject) => {
                const { sortBy, descending, page, rowsPerPage } = this.pagination

                let items = this.fetchData()
                const total = items.length

                //some code to setup pagination and sort

                setTimeout(() => {
                    this.loading = false
                    resolve({
                    items,
                    total
                    })
                    }, 1000)
            })
        }
    },
})

Upvotes: 5

Views: 23956

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1073978

Updated answer:

Apologies, in my original answer below I completely missed this:

So far I set a this.items inside fetchData but I realize it's absolutely not what I want to actually do.

My answer assumed you did want this.items. Sorry about that.

To just get the info from fetchData without using this.items, use the then handler on the promise from axios.get to return a new promise (remember, then and catch return promises) that resolves with response.data.data:

fetchData() {
//  vvvvvv------------------------
    return axios.get('/api/v1/translations').then(response => {
        return response.data.data;
//      ^^^^^^^^^^^^^^^^^^^^^^^^^
    })
},

This can be written more succinctly:

fetchData() {
    return axios.get('/api/v1/translations').then(response => response.data.data);
},

Then, in getDataFromApi, use a then handler on that promise:

getDataFromApi () {
    this.loading = true
    return this.fetchData().then(items => {
        // You might use this at some stage: const { sortBy, descending, page, rowsPerPage } = this.pagination
        this.loading = false;
        return { items, total: items.length };
    });
}

Note that it's important that the consumer of getDataFromApi handles promise rejection (e.g., uses a catch handler on it). If you don't want the consumer to do that, don't return the promise, and use a catch within getDataFromApi instead.


Original answer:

You return the promise from axios.get:

fetchData() {
//  vvvvvv------------------------
    return axios.get('/api/v1/translations').then(response => {
        //console.log(response.data)
        this.items = response.data.data
    })
},

...and then use it rather than creating a new one in getFromApi:

getDataFromApi () {
    this.loading = true
    return this.fetchData().then(() => {
        // You might use this at some stage: const { sortBy, descending, page, rowsPerPage } = this.pagination
        this.loading = false;
        return { items: this.items, total: this.items.length };
    });
}

Remember that then (and catch) create new promises; there's no reason for new Promise if you already have a promise to work with. More on that part: What is the explicit promise construction antipattern and how do I avoid it?

(Note that I'm assuming you want this.items on the instance for some reason. If you didn't, you'd just want to use response.data as the return value of your axios.get...then callback and use it in getDataFromApi.)

Upvotes: 6

Related Questions