How can I use async/await in the Vue 3.0 setup() function using Typescript

20,491

Solution 1

Try to use onMounted hook to manipulate asynchronous call :

 setup() {
    const users = ref([]);
    onMounted(async () => {
      const res = await axios.get("https://jsonplaceholder.typicode.com/users");
      users.value = res.data;
      console.log(res);
    });

    return {
      users,
    };
  },

LIVE DEMO

But the Best approach is to use async setup in child component and wrap that component by Suspense component in the parent one :

UserList.vue

<script  lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
    async setup() {
        //get users from jsonplacerholder using await fetch api 
        const users = await fetch("https://jsonplaceholder.typicode.com/users").then(res => res.json());

        return {
            users
        }
    }
})
</script>
<template>
    <div>
        <!-- list users -->
        <ul>
            <li v-for="user in users">{{ user.name }}</li>
        </ul>
    </div>
</template>

Parent component:

<script lang="ts">

import UserList from "../components/tmp/UserList.vue";
...
</script>

   <div>
            <!-- Suspense component  to show users  -->
            <Suspense>
                <template #fallback>
                    <div>loading</div>
                </template>

                <UserList />
            </Suspense>
        </div>

Solution 2

Another way of doing this:

 const users = ref([]);
 
 (async () => {
   const res = await axios.get("https://jsonplaceholder.typicode.com/users");
   users.value = res.data;
   console.log(res);
 })()

 return {
   users,
 }

And you don't have to wait for it to mount, this is similar to using created() with the options API.

Note: Don't forget to always have a semicolon ";" before the function statement, otherwise, JavaScript would think that the previous statement was supposed to return a function, the following code, for example, would cause a bug "ref([]) is not a function":

const users = ref([]) // No semicolon here

(async () => {

Another way of preventing this bug is to always have the semicolon on the same line of the function definition, the following code also works:

;(async () => {

Solution 3

An alternative is to use the promise chain, the benefit for doing so is that the code is run even before the beforeCreate lifecycle hook:

import { defineComponent, ref } from 'vue'
import { getData } from './api.js'

export default defineComponent({
  setup() {
    const users = ref([])

    getData().then(({ data }) => (users.value = data))

    return {
      users,
    }
  },
})
Share:
20,491
Hendrik Jan
Author by

Hendrik Jan

I work as a web-application developer at a university in the Netherlands. My main interests are open source programming languages like Javascript, PHP and MySQL. For my daily job I use CodeIgniter and jQuery. For my pet projects I use Laravel, Dojo and Dart. I also used to use Couchdb and Lotus Notes, but not anymore.

Updated on September 21, 2021

Comments

  • Hendrik Jan
    Hendrik Jan over 2 years

    (This question has been answered for JavaScript, see below, but this question is specific for TypeScript, which behaves differently)

    I'm trying to use async functionality in Vue3.0 using typescript.

    Without async this code works nice:

    // file: components/HelloWorld.vue
    
    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
      </div>
    </template>
    
    <script lang="ts">
    import {defineComponent} from 'vue'
    
    export default defineComponent({
      name: 'HelloWorld',
      props: {
        msg: String,
      },
      async setup() { // <-- this works without 'async'
        const test = 'test'
    
        // await doSomethingAsynchronous()
    
        return {
          test,
        }
      },
    })
    </script>
    

    With async setup() the component "HelloWorld" disappears from the page, and the Firefox console tells me

    "Uncaught (in promise) TypeError: node is null (runtime-dom.esm-bundler.js)"
    

    When I change async setup() to setup(), the code works, but then I would not be able to use async/await inside the setup function.

    So my question: how do I use async/await inside the setup() function using Typescript?

    EDIT:

    The answer to this question: why i got blank when use async setup() in Vue3 shows that async setup() does work with JavaScript, so I would expect it to work in TypeScript as well.

  • Onno van der Zee
    Onno van der Zee almost 3 years
    Much cleaner! And beware not to put the executing brackets inside the grouping brackets: you will get a TS1128: Declaration or statement expected.
  • basickarl
    basickarl about 2 years
    When you create a immediately invoked function you split the thread into two running in parallel. This answer will always return an empty users and THEN update it. You should be using suspense and async setup instead. Unless you want this buggy experience of updating the UI.
  • gustavodacrvi
    gustavodacrvi about 2 years
    Keep in mind that suspense is still considered an "Experimental feature" though. vuejs.org/guide/built-ins/suspense.html