8000 feat(nuxt): experimental native async context support by pi0 Β· Pull Request #20918 Β· nuxt/nuxt Β· GitHub
[go: up one dir, main page]

Skip to content

feat(nuxt): experimental native async context support #20918

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Aug 7, 2023

Conversation

pi0
Copy link
Member
@pi0 pi0 commented May 17, 2023

πŸ”— Linked issue

Related to #14269

❓ Type of change

  • πŸ“– Documentation (updates to the documentation, readme or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • πŸ‘Œ Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

πŸ“š Description

See #14269 (comment) and unjs/unctx#async-context for some information.

When using async composables, Nuxt app context will be only available before the first await statement, and for nested composables, it should be manually passed which is a negative DX especially as composables are designed to have access to the context without manually passing it!

Previously we had done some efforts in order to mitigate this DX issue:

  • Vue uses transform magic in <script setup> to support async/await syntax
  • Nuxt uses asyncTransform for defineNuxtPlugin, defineNuxtRouteMiddleware, defineNuxtComponent (setup and asyncData keys), defineNuxtPlugin (setup key) and definePageMeta (middleware and validate keys) for one level async support via transforms similar to Vue (configurable/extendable via optimization.asyncTransforms option)
  • Manual nuxtApp.runWithContext(() => ...) helper to make async context available (still one level only)

Developing more complex logic and nested async composables, makes it harder and harder to use previous solutions. A simple and problematic example:

const delay = () => new Promise(resolve => setTimeout(resolve, 10))

export async function nestedAsyncComposable () {
  await delay()
  return await fn1()
}

async function fn1 () {
  await delay()
  return await fn2()
}

async function fn2 () {
  await delay()
  // This fails with nuxt context unavailable error!
  const app = useNuxtApp()
  // Some logic here
}

This PR enables an experimental feature using Node.js AsyncLocalStorage and new unctx support to make async context available natively to any nested async composable without needing a transform or manual passing/calling with context.

Because AsyncLocalStorage is required for this feature and it is a Node.js only feature, we have already made a mock available via unjs/unenv for non-Node.js deployment presets (unjs/unenv#98).

Also since this is a critical and new feature, I have made it available under an experimental flag to be enabled:

export default defineNuxtConfig({
  experimental: {
    asyncContext: true
  }
})

TODO:

Notes:

  • This feature is limited to Node.js for now but there is an TC39 proposal ongoing to support native context in other platforms. Also Cloudflare workers recently announced Async Context Tracking! (upstream tracker: Enable cloudflare Node.js compatibility layerΒ nitrojs/nitro#1171)
  • This is only first step in adopting native async context. Using this feature, we can extend composables to all server routes and nitro/h3 utils soon πŸš€
  • (investigating yet to workaround) When using from <script setup>, we need to call first level composable with nuxtApp.runWithContext since Vue itself does not make async context available or has async context support.

πŸ“ Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

8000
Copy link
Member
danielroe commented Jun 4, 2023

@pi0 Reverted b1cae6f (#20918) just in case the commit was purely intended to fix the test failure - feel free to reapply it.

reverted

@danielroe
Copy link
Member

@pi0 What do you mean by:

Try to find a way to prevent first nuxtApp.runWithContext requirement

Is there something I can help with on this?

@pi0
Copy link
Member Author
pi0 commented Jul 21, 2023

@danielroe I have tried to explain it in the foot notes. ALS only works when we use nuxtApp.runWithContext once. We need to find a way to call this on the whole renderer (in nitro renderer maybe or app init) to track the async context. If you could find a working solution it is more than welcome!

@danielroe danielroe marked this pull request as ready for review August 1, 2023 10:37
@danielroe danielroe added this to the 3.7 milestone Aug 1, 2023
@danielroe danielroe changed the title feat: experimental native async context support feat(nuxt): experimental native async context support Aug 1, 2023
@sholohov
Copy link
sholohov commented Aug 15, 2024

image

Hello. Is it possible to use the experimental async context flag and not be afraid that it will be removed in some new version of Nuxt? This is very important for me.

@pi0
Copy link
Member Author
pi0 commented Aug 15, 2024

Hello. Is it possible to use the experimental async context flag and not be afraid that it will be removed in some new version of Nuxt? This is very important for me.

Yes. Actually the only reason it is not universally enabled is that because some platforms don't support it yet. As soon as it is universal we move it out of the experimental state.

@moshetanzer
Copy link
Contributor

yip, still doesnt work in vercel! but looking forward...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants
0