25
<script lang="ts">
import { createComponent } from "@vue/composition-api";

import { SplashPage } from "../../lib/vue-viewmodels";

export default createComponent({
  async setup(props, context) {
    await SplashPage.init(2000, context.root.$router, "plan", "login");
  }
});
</script>

ERROR: "setup" must return a "Object" or a "Function", got "Promise"

邵励治
  • 251
  • 1
  • 4
  • 6

2 Answers2

18

The setup function must be synchronous can be async using Suspense.

How to avoid using async setup (obsolete answer)

An onMounted hook can be used with an async callback:

import { onMounted } from "@vue/composition-api";

// …
export default createComponent({
  setup(props, context) {
    onMounted(async () => {
      await SplashPage.init(2000, context.root.$router, "plan", "login");
    )};
  }
});

Or, it is always possible to call an asynchronous function without to await it:

SplashPage.init(2000, context.root.$router, "plan", "login")
  .catch(console.log);

In both cases, you'll have to take into account that the component will be rendered before the execution of the asynchronous function. A simple way to not display something that would depends on it is to use v-if in your template.

Paleo
  • 21,831
  • 4
  • 65
  • 76
  • Must be synchronous you say, what about [this async setup](https://github.com/vuejs/vue-router-next/blob/master/playground/views/ComponentWithData.vue#L15)? – Greendrake Aug 29 '20 at 12:50
  • @Greendrake You're right. I see that there is a way now, with `Suspense`. I will give a try to this new feature then I will come back here to update my answer. https://vueschool.io/articles/vuejs-tutorials/suspense-new-feature-in-vue-3/ – Paleo Sep 02 '20 at 16:40
  • 5
    that feature unfortunately will only work with vue3 not the vue2 composition-api – relief.melone Apr 11 '21 at 20:33
0

I have another solution that works in my use-case. Maybe it will help. It is a bit like the lifestyle hook approach, but without needing it. It also doesn't need the <Suspense> tag which was "overkill" in my use-case.

The idea is to return a default value (the empty array in this case, but could be a "Loading..." splash page). Then, after the async has resolved, update the reactive prop (menuItems array here, but it could be the actual splash page contents or html or whatever).

I know this might not suit all use-cases but it is another approach that works.

Simplified code:

setup () {
  const menuItems = ref([])

  const buildMenuItems = async () => {
     // eg get items from server, return an array of formatted items...
  }
  
  /* setTimeout(async () => {
    menuItems.value = await buildMenuItems()
  }, 0) */ 

  // or..
  ;(async () => {
    menuItems.value = await buildMenuItems()
  })()  

  return {
    menuItems
  } 
}

I tested it by making buildMenuItems() take 2 seconds and it all works fine.

EDIT: And then I discovered other ways (even for non TypeScript): How can I use async/await in the Vue 3.0 setup() function using Typescript

Cheers, Murray

Murrah
  • 1,508
  • 1
  • 13
  • 26