-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
fix(nuxt): ensure newKey asyncData runs when oldKey asyncData is still running #32466
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
Conversation
|
@nuxt/kit
nuxt
@nuxt/rspack-builder
@nuxt/schema
@nuxt/vite-builder
@nuxt/webpack-builder
commit: |
Maybe this should be solved here instead: nuxt/packages/nuxt/src/app/composables/fetch.ts Lines 144 to 145 in 8668208
|
CodSpeed Performance ReportMerging #32466 will not alter performanceComparing Summary
|
Alternatively, setting EDIT: now I wonder if this was intended https://nuxt.com/docs/4.x/api/nuxt-config#alwaysrunfetchonkeychange |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR ensures that when changing asyncData keys, a new fetch is triggered even if the previous key’s asyncData call is still pending, fixing issue #32109.
- Introduces an
isRunning
flag to detect ongoing asyncData promises and include it in the execution condition - Adds an API fixture (
/api/wait/:ms
) and a new E2E test for theuseFetch
composable withimmediate: false
- Updates related test fixtures and pages to verify parallel fetch execution
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
File | Description |
---|---|
test/fixtures/basic/server/api/wait/[ms].get.ts | Adds a /api/wait/:ms endpoint fixture |
test/fixtures/basic/app/pages/composables/fetch-immediate-false.vue | Implements a page using useFetch with immediate: false |
test/e2e/composables.test.ts | E2E test for parallel fetch execution |
packages/nuxt/src/app/composables/asyncData.ts | Core change: include isRunning in asyncData watcher |
Comments suppressed due to low confidence (2)
test/e2e/composables.test.ts:7
- [nitpick] The comment describes testing the runtime compiler, but this suite is focused on the
useFetch
composable behavior; please update the description to match its actual purpose.
* This test suite verifies that Vue's runtime compiler works correctly within Nuxt,
test/fixtures/basic/server/api/wait/[ms].get.ts:6
- The message uses 'hey' which is inconsistent with the endpoint name '/wait'; consider renaming it to something like
Hello from wait after ${ms}ms
for clarity.
message: `Hello from hey after ${ms}ms`,
@@ -327,6 +327,7 @@ export function useAsyncData< | |||
const unsubExecute = watch([key, ...(options.watch || [])], ([newKey], [oldKey]) => { | |||
if ((newKey || oldKey) && newKey !== oldKey) { | |||
const hasRun = nuxtApp._asyncData[oldKey]?.data.value !== undefined | |||
const isRunning = nuxtApp._asyncDataPromises[oldKey] !== undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add unit tests (e.g., using Vitest) for the new key-change logic in useAsyncData
to cover cases where isRunning
is true, as required by our core functionality testing guidelines.
Copilot uses AI. Check for mistakes.
WalkthroughA modification was made to the client-side watcher in the 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms (3)
✨ Finishing Touches
🧪 Generate Unit Tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
test/fixtures/basic/server/api/wait/[ms].get.ts (1)
1-10
: LGTM! Consider adding type safety for the ms parameter.The API route implementation is correct and serves its purpose for testing delayed responses. The graceful handling of invalid
ms
parameters is good.Consider adding type validation for the parameter:
export default defineEventHandler((event) => { const ms = getRouterParam(event, 'ms') + const delay = ms ? Math.max(0, parseInt(ms, 10)) || 0 : 0 return new Promise((resolve) => { setTimeout(() => { resolve({ - message: `Hello from hey after ${ms}ms`, + message: `Hello from hey after ${delay}ms`, }) - }, ms ? +ms : 0) + }, delay) }) })test/fixtures/basic/app/pages/composables/fetch-immediate-false.vue (1)
12-24
: LGTM! Well-structured test component that follows Vue best practices.The component correctly uses the Composition API with
<script setup>
and properly tests the fetch behavior withimmediate: false
. The watcher configuration with{ once: true }
effectively simulates the scenario where a key change should trigger a new fetch.Consider adding a small delay to better simulate real-world timing scenarios:
-watch(query, () => execute(), { once: true }) +watch(query, () => nextTick(() => execute()), { once: true })test/e2e/composables.test.ts (1)
34-44
: Comprehensive e2e test that validates the fix effectively.The test properly validates the new behavior where fetch execution is triggered on key changes even when the previous fetch might still be running. The step-by-step assertions cover the complete lifecycle from idle to success state.
Consider making the test name more descriptive of the specific scenario being tested:
-test('should handle parallel fetch execute with immediate: false and `experimental.alwaysRunFetchOnKeyChange: false`', async ({ page, goto }) => { +test('should trigger new fetch on key change when previous fetch is still running with immediate: false', async ({ page, goto }) => {The test logic is solid with appropriate assertions for status transitions and error handling.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/nuxt/src/app/composables/asyncData.ts
(2 hunks)test/e2e/composables.test.ts
(1 hunks)test/fixtures/basic/app/pages/composables/fetch-immediate-false.vue
(1 hunks)test/fixtures/basic/server/api/wait/[ms].get.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`**/*.ts`: Follow standard TypeScript conventions and best practices
**/*.ts
: Follow standard TypeScript conventions and best practices
📄 Source: CodeRabbit Inference Engine (.github/copilot-instructions.md)
List of files the instruction was applied to:
test/fixtures/basic/server/api/wait/[ms].get.ts
packages/nuxt/src/app/composables/asyncData.ts
test/e2e/composables.test.ts
`**/*.vue`: Use <script setup lang="ts"> and the composition API when creating Vue components
**/*.vue
: Use <script setup lang="ts"> and the composition API when creating Vue components
📄 Source: CodeRabbit Inference Engine (.github/copilot-instructions.md)
List of files the instruction was applied to:
test/fixtures/basic/app/pages/composables/fetch-immediate-false.vue
⏰ Context from checks skipped due to timeout of 90000ms (20)
- GitHub Check: test-fixtures (windows-latest, built, webpack, default, manifest-on, json, 20)
- GitHub Check: test-fixtures (windows-latest, built, webpack, async, manifest-on, json, 20)
- GitHub Check: test-fixtures (windows-latest, built, rspack, default, manifest-on, json, 20)
- GitHub Check: test-fixtures (windows-latest, built, vite, default, manifest-off, json, 20)
- GitHub Check: test-fixtures (windows-latest, dev, vite, default, manifest-on, json, 20)
- GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-on, json, 20)
- GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-on, json, 20)
- GitHub Check: test-fixtures (windows-latest, dev, vite, default, manifest-off, json, 20)
- GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-off, json, 20)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, js, 20)
- GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-off, json, 20)
- GitHub Check: test-fixtures (ubuntu-latest, built, webpack, default, manifest-on, json, 20)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, json, 20)
- GitHub Check: test-fixtures (ubuntu-latest, built, webpack, async, manifest-on, json, 20)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, js, 20)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, 20)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, json, 20)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-off, json, 20)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-on, json, 20)
- GitHub Check: test-benchmark
🔇 Additional comments (2)
packages/nuxt/src/app/composables/asyncData.ts (2)
330-330
: Excellent addition to track running state.This line correctly identifies when the old key has an ongoing async operation by checking for an existing promise in
_asyncDataPromises
.
341-341
: Perfect fix for the blocking asyncData issue.This change ensures that a new fetch is triggered not only when
immediate
is true or when the previous key's data has already run (hasRun
), but also when there's still an ongoing fetch for the old key (isRunning
). This resolves the core issue where newKey asyncData was blocked by ongoing oldKey operations.The logic is sound and maintains backward compatibility whilst improving responsiveness.
test/e2e/composables.test.ts
Outdated
import { fileURLToPath } from 'node:url' | ||
import { isWindows } from 'std-env' | ||
import { join } from 'pathe' | ||
import { expect, test } from './test-utils' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there any chance we can use a nuxt runtime test rather than an e2e test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@OrbisK you should be able to do it by mocking _asyncPromises
. I can try to do it if you want
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@huang-julien that would be nice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@huang-julien I have it! 💚
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great 🔥 !
f1b70c1
to
cf6a159
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
test/nuxt/composables.test.ts (1)
927-927
: Consider consistency in conditional test execution.The conditional logic uses
skipIf(process.env.NUXT_LEGACY !== '1')
whilst other legacy tests in this file userunIf(process.env.PROJECT === 'nuxt-legacy')
. This inconsistency could lead to confusion about when tests execute.Consider using the same pattern for consistency:
- it.skipIf(process.env.NUXT_LEGACY !== '1')('should handle parallel execute with `immediate: false`', async () => { + it.runIf(process.env.NUXT_LEGACY === '1')('should handle parallel execute with `immediate: false`', async () => {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/nuxt/src/app/composables/asyncData.ts
(2 hunks)test/nuxt/composables.test.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/nuxt/src/app/composables/asyncData.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.ts`: Follow standard TypeScript conventions and best practices
**/*.ts
: Follow standard TypeScript conventions and best practices
📄 Source: CodeRabbit Inference Engine (.github/copilot-instructions.md)
List of files the instruction was applied to:
test/nuxt/composables.test.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: Tofandel
PR: nuxt/nuxt#0
File: :0-0
Timestamp: 2024-11-11T12:34:22.648Z
Learning: Ensure that AI-generated summaries accurately reflect the key changes in the PR, focusing on notable changes such as the removal of unused imports and variables starting with underscores.
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:
```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```
instead of wrapping the import in a computed property or importing the component unconditionally.
test/nuxt/composables.test.ts (1)
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-23T19:10:10.027Z
Learning: Write end-to-end tests using Playwright and @nuxt/test-utils to validate application behavior from the user's perspective.
🔇 Additional comments (1)
test/nuxt/composables.test.ts (1)
928-951
: Excellent test implementation for parallel execution behaviour.This test effectively validates the core issue addressed in the PR - ensuring that
useFetch
withimmediate: false
properly handles parallel executions triggered by reactive changes. The test structure is sound:
- Uses reactive objects to simulate real-world scenarios
- Properly validates status transitions from 'idle' → 'pending' → 'success' → 'pending'
- Employs
vi.waitFor
for reliable async state validation- Uses
expect.soft
for better debugging when assertions failThe test logic correctly simulates the scenario where multiple rapid changes to reactive dependencies should trigger new fetches even when previous ones are still running.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you ❤
🔗 Linked issue
fixes #32109
📚 Description
This PR should fix #32109. It ensures that the newKey asyncdata is not only executed once the request for the old key has finished.
I'll still mark it as a draft so that I can check whether there is a better solution.
TODO: add testsNote
It was not possible for me to reproduce this test as runtime or fixtures test. Only with full e2e test.