Skip to content

fix(runtime-core): skip async component callbacks after unmount#14911

Merged
edison1105 merged 2 commits into
vuejs:mainfrom
baozjj:fix/async-component-callback-after-unmount
Jun 11, 2026
Merged

fix(runtime-core): skip async component callbacks after unmount#14911
edison1105 merged 2 commits into
vuejs:mainfrom
baozjj:fix/async-component-callback-after-unmount

Conversation

@baozjj

@baozjj baozjj commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

What is the motivation / use case?

Example: A route lazy-loads a heavy chart with defineAsyncComponent({ timeout: 10000 }). The user opens the page, sees the loading state, then navigates away after 2 seconds. About 8 seconds later, the app is already on another page, but the console / global errorHandler (e.g. Sentry) still reports Async component timed out after 10000ms. — even though that async component has already been unmounted.

In the non-Suspense setup path, delay / timeout timers and load().then() / load().catch() do not check instance.isUnmounted, and timers are not cleared on unmount. That can trigger onError()handleError() after teardown. The __asyncHydrate path already uses !instance.isUnmounted; this change aligns the setup path with that behavior.

What is the solution?

  • Register onUnmounted to clearTimeout for delay and timeout timers
  • In delay, timeout, and load() resolve/reject handlers, return early when instance.isUnmounted

What is the scope of this change?

packages/runtime-core/src/apiAsyncComponent.ts and regression tests in apiAsyncComponent.spec.ts only.

Test plan

  • pnpm test packages/runtime-core/__tests__/apiAsyncComponent.spec.ts --run (24 passed)
  • should not call errorHandler after unmount (timeout) — unmount before timeout fires
  • should not call errorHandler after unmount (loader error) — unmount before rejected loader is handled

Summary by CodeRabbit

  • Bug Fixes

    • Prevented async components from triggering app error handlers after they've been unmounted (including when pending, timing out, or when a previous loader rejects).
  • Tests

    • Added tests covering unmount-before-timeout, unmount-before-loader-rejection, and remount-after-ignored-rejection scenarios to ensure error handlers aren't called spuriously.

Clear delay/timeout timers on unmount and guard load/timeout handlers
with isUnmounted to avoid spurious errorHandler invocations.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ed0c020f-bacc-48fe-997f-a8183e7ca0d9

📥 Commits

Reviewing files that changed from the base of the PR and between 767d392 and d06aba4.

📒 Files selected for processing (2)
  • packages/runtime-core/__tests__/apiAsyncComponent.spec.ts
  • packages/runtime-core/src/apiAsyncComponent.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/runtime-core/src/apiAsyncComponent.ts

📝 Walkthrough

Walkthrough

defineAsyncComponent now clears delay/timeout timers on unmount and returns early from loader resolution/rejection if the instance is unmounted; tests added to ensure app.config.errorHandler is not called when async components unmount before timeout or loader rejection, and to verify remount behavior.

Changes

Async component unmount lifecycle guards

Layer / File(s) Summary
Timer cleanup and loader state guards
packages/runtime-core/src/apiAsyncComponent.ts
Imports onUnmounted, introduces delayTimer and timeoutTimer cleared on unmount, and adds instance.isUnmounted early returns in loader success and error paths to prevent state updates and onError calls after unmount.
Unmount lifecycle test coverage
packages/runtime-core/__tests__/apiAsyncComponent.spec.ts
Adds three tests: timeout-unmount (unmount before timeout fires), loader-reject-unmount (unmount before loader rejects), and remount-after-unmount (ignore rejected loader, remount triggers loader again and resolves), asserting app.config.errorHandler is not invoked.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

:hammer: p3-minor-bug, ready for review

Suggested reviewers

  • edison1105

Poem

🐰 Timers cleared with gentle paws,
Unmounted leaves avoid the cause,
No phantom errors wake the night,
Loaders hush and keep things right,
I hop away, the app sleeps tight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(runtime-core): skip async component callbacks after unmount' directly and concisely summarizes the main change: preventing async component callbacks from executing after the component unmounts.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new

pkg-pr-new Bot commented Jun 3, 2026

Copy link
Copy Markdown

Open in StackBlitz

@vue/compiler-core

pnpm add https://pkg.pr.new/@vue/compiler-core@14911
npm i https://pkg.pr.new/@vue/compiler-core@14911
yarn add https://pkg.pr.new/@vue/compiler-core@14911.tgz

@vue/compiler-dom

pnpm add https://pkg.pr.new/@vue/compiler-dom@14911
npm i https://pkg.pr.new/@vue/compiler-dom@14911
yarn add https://pkg.pr.new/@vue/compiler-dom@14911.tgz

@vue/compiler-sfc

pnpm add https://pkg.pr.new/@vue/compiler-sfc@14911
npm i https://pkg.pr.new/@vue/compiler-sfc@14911
yarn add https://pkg.pr.new/@vue/compiler-sfc@14911.tgz

@vue/compiler-ssr

pnpm add https://pkg.pr.new/@vue/compiler-ssr@14911
npm i https://pkg.pr.new/@vue/compiler-ssr@14911
yarn add https://pkg.pr.new/@vue/compiler-ssr@14911.tgz

@vue/reactivity

pnpm add https://pkg.pr.new/@vue/reactivity@14911
npm i https://pkg.pr.new/@vue/reactivity@14911
yarn add https://pkg.pr.new/@vue/reactivity@14911.tgz

@vue/runtime-core

pnpm add https://pkg.pr.new/@vue/runtime-core@14911
npm i https://pkg.pr.new/@vue/runtime-core@14911
yarn add https://pkg.pr.new/@vue/runtime-core@14911.tgz

@vue/runtime-dom

pnpm add https://pkg.pr.new/@vue/runtime-dom@14911
npm i https://pkg.pr.new/@vue/runtime-dom@14911
yarn add https://pkg.pr.new/@vue/runtime-dom@14911.tgz

@vue/server-renderer

pnpm add https://pkg.pr.new/@vue/server-renderer@14911
npm i https://pkg.pr.new/@vue/server-renderer@14911
yarn add https://pkg.pr.new/@vue/server-renderer@14911.tgz

@vue/shared

pnpm add https://pkg.pr.new/@vue/shared@14911
npm i https://pkg.pr.new/@vue/shared@14911
yarn add https://pkg.pr.new/@vue/shared@14911.tgz

vue

pnpm add https://pkg.pr.new/vue@14911
npm i https://pkg.pr.new/vue@14911
yarn add https://pkg.pr.new/vue@14911.tgz

@vue/compat

pnpm add https://pkg.pr.new/@vue/compat@14911
npm i https://pkg.pr.new/@vue/compat@14911
yarn add https://pkg.pr.new/@vue/compat@14911.tgz

commit: d06aba4

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 106 kB (+155 B) 40.1 kB (+61 B) 36.1 kB (+46 B)
vue.global.prod.js 164 kB (+155 B) 60.1 kB (+58 B) 53.5 kB (-1 B)

Usages

Name Size Gzip Brotli
createApp (CAPI only) 48.8 kB 19 kB 17.4 kB
createApp 56.9 kB 22 kB 20.1 kB
createSSRApp 61.2 kB 23.8 kB 21.7 kB
defineCustomElement 63.1 kB 23.9 kB 21.8 kB
overall 71.7 kB 27.4 kB 25 kB

@edison1105 edison1105 added ready to merge The PR is ready to be merged. 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. labels Jun 4, 2026
@edison1105

Copy link
Copy Markdown
Member

/ecosystem-ci run

@vue-bot

vue-bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

📝 Ran ecosystem CI: Open

suite result latest scheduled
primevue success success
pinia success success
vitepress success success
vue-i18n success failure
nuxt success success
router success failure
radix-vue failure failure
vue-simple-compiler success failure
test-utils success success
language-tools success success
vite-plugin-vue success success
vuetify success failure
vueuse success failure
vue-macros success failure
quasar success success
vant success success

@edison1105 edison1105 merged commit 5300ead into vuejs:main Jun 11, 2026
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. ready to merge The PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants