[resolved] Why using await inside composables cause them to break?
I believe my issue is similar to this https://discord.com/channels/473401852243869706/1141010505482379265
My original use case involved combining multiple composables, in which some composables depend on a value from others which I'd like to
await
to make sure data is rendered server side.
I narrowed the issue to this:
/app.vue
/composables/useMessage.ts
/pages/index.vue
Running the app with the above code causes:
* a WARN [Vue warn]: inject() can only be used inside setup() or functional components.
* The value of 'message'
is undefined
Now, if I omit the await Promise.resolve()
line from the composable. Everything works as expected including the value of 'message'
being 'hello!'
.
Could you explain why does this happen? I can't make sense of it, because in my understanding this shouldn't be different than a synchronous statement.3 Replies
Hi @amazonaffected. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
----------------------
Your question is about a specific issue with using
await
inside Nuxt composables, which is indeed a common problem in Nuxt 3. I'll explain why this happens based on the information provided in the knowledge sources.
The core reason for this behavior is related to how Vue and Nuxt handle the context during asynchronous operations. As explained in the Nuxt documentation (https://nuxt.com/docs/guide/concepts/auto-imports#vue-and-nuxt-composables):
"During a component lifecycle, Vue tracks the temporary instance of the current component (and similarly, Nuxt tracks a temporary instance of nuxtApp
) via a global variable, and then unsets it in same tick. This is essential when server rendering, both to avoid cross-request state pollution (leaking a shared reference between two users) and to avoid leakage between different components."
When you use await
inside a composable, it creates an asynchronous break in the execution. After this break, the Vue/Nuxt context is no longer available, which is why inject()
fails to work correctly. This is further explained in the Nuxt documentation (https://nuxt.com/docs/api/composables/use-nuxt-app#a-deeper-explanation-of-context):
"The Composition API and Nuxt Composables are only available during lifecycle and in same tick before any async operation."
To illustrate this, the documentation provides an example:
In your case, await Promise.resolve()
is causing this context loss, even though it's a minimal async operation.
To solve this issue, there are several approaches you can take:
1. Avoid using await
in composables. This is the simplest solution but may not always be feasible.
2. Use runWithContext
or callWithNuxt
to restore the context. As mentioned in the Nuxt documentation (https://nuxt.com/docs/api/composables/use-nuxt-app#solution), these functions can be used to restore the context after an async operation.
3. Use the experimental asyncContext
feature. This is mentioned in the Nuxt documentation (https://nuxt.com/docs/guide/concepts/auto-imports#vue-and-nuxt-composables), but it's still experimental and may not be suitable for production use.
4. Restructure your code to perform async operations last, as suggested by Anthony Fu and mentioned in the YouTube video (https://www.youtube.com/watch?v=ofuKRZLtOdY) about this issue.
For your specific use case, you might want to consider restructuring your composable to avoid the await
or use runWithContext
if you absolutely need the async operation. Remember that this issue is a known limitation in the current design of Vue and Nuxt composables, and the Nuxt team is working on improving this in future versions.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------<@1188032231105630269> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Impressive! This pretty much answered everything I wanted to know regarding this issue. Great job to everyone working on this AI / Knowledge base, thank you!
I'm not sure if this will work, but I will try to use all await statements at the end in compination with
ref()
/ computed()
values.